From 3309d881989abe5fb630631ccd7333daa3c1707a Mon Sep 17 00:00:00 2001 From: Enrico Granata Date: Sat, 12 Jan 2013 01:00:22 +0000 Subject: [PATCH] Providing a data formatter for libc++ std::wstring In the process, refactoring the std::string data formatter to be written in C++ so that commonalities between the two can be exploited Also, providing a new API on the ValueObject to navigate a hierarchy by index-path Lastly, an appropriate test case is included llvm-svn: 172282 --- .../include/lldb/Core/CXXFormatterFunctions.h | 6 + lldb/include/lldb/Core/ValueObject.h | 17 +++ lldb/source/Core/CXXFormatterFunctions.cpp | 103 ++++++++++++++++-- lldb/source/Core/FormatManager.cpp | 12 +- lldb/source/Core/ValueObject.cpp | 80 ++++++++++++++ .../data-formatter-stl/libcxx/string/Makefile | 8 ++ .../string/TestDataFormatterLibcxxString.py | 81 ++++++++++++++ .../data-formatter-stl/libcxx/string/main.cpp | 12 ++ 8 files changed, 310 insertions(+), 9 deletions(-) create mode 100644 lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/Makefile create mode 100644 lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py create mode 100644 lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp diff --git a/lldb/include/lldb/Core/CXXFormatterFunctions.h b/lldb/include/lldb/Core/CXXFormatterFunctions.h index 3373b9ccb323..9c4eaf12feeb 100644 --- a/lldb/include/lldb/Core/CXXFormatterFunctions.h +++ b/lldb/include/lldb/Core/CXXFormatterFunctions.h @@ -49,6 +49,12 @@ namespace lldb_private { bool WCharStringSummaryProvider (ValueObject& valobj, Stream& stream); // wchar_t* + bool + LibcxxStringSummaryProvider (ValueObject& valobj, Stream& stream); // libc++ std::string + + bool + LibcxxWStringSummaryProvider (ValueObject& valobj, Stream& stream); // libc++ std::wstring + template bool NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream); diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index ae57ac88f458..244cb2cf1736 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -734,6 +734,23 @@ public: virtual lldb::ValueObjectSP GetChildAtIndex (uint32_t idx, bool can_create); + // this will always create the children if necessary + lldb::ValueObjectSP + GetChildAtIndexPath (const std::initializer_list &idxs, + uint32_t* index_of_error = NULL); + + lldb::ValueObjectSP + GetChildAtIndexPath (const std::vector &idxs, + uint32_t* index_of_error = NULL); + + lldb::ValueObjectSP + GetChildAtIndexPath (const std::initializer_list< std::pair > &idxs, + uint32_t* index_of_error = NULL); + + lldb::ValueObjectSP + GetChildAtIndexPath (const std::vector< std::pair > &idxs, + uint32_t* index_of_error = NULL); + virtual lldb::ValueObjectSP GetChildMemberWithName (const ConstString &name, bool can_create); diff --git a/lldb/source/Core/CXXFormatterFunctions.cpp b/lldb/source/Core/CXXFormatterFunctions.cpp index cd4e227b86d7..e9863de7e70f 100644 --- a/lldb/source/Core/CXXFormatterFunctions.cpp +++ b/lldb/source/Core/CXXFormatterFunctions.cpp @@ -158,8 +158,9 @@ ReadUTFBufferAndDumpToStream (uint64_t location, // if not UTF8, I need a conversion function to return proper UTF8 if (origin_encoding != 8 && !ConvertFunction) return false; - const int bufferSPSize = 1024; - const int sourceSize = (bufferSPSize / (origin_encoding >> 2)); + + const int sourceSize = process_sp->GetTarget().GetMaximumSizeOfStringSummary(); + const int bufferSPSize = sourceSize * (origin_encoding >> 2); Error error; lldb::DataBufferSP buffer_sp(new DataBufferHeap(bufferSPSize,0)); @@ -218,6 +219,9 @@ ReadUTFBufferAndDumpToStream (uint64_t location, utf8_data_end_ptr = (UTF8*)data_end_ptr; } + // since we tend to accept partial data (and even partially malformed data) + // we might end up with no NULL terminator before the end_ptr + // hence we need to take a slower route and ensure we stay within boundaries for (;utf8_data_ptr != utf8_data_end_ptr; utf8_data_ptr++) { if (!*utf8_data_ptr) @@ -290,9 +294,14 @@ lldb_private::formatters::WCharStringSummaryProvider (ValueObject& valobj, Strea if (!process_sp) return false; - lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + lldb::addr_t data_addr = 0; + + if (valobj.IsPointerType()) + data_addr = valobj.GetValueAsUnsigned(0); + else if (valobj.IsArrayType()) + data_addr = valobj.GetAddressOf(); - if (!valobj_addr) + if (data_addr == 0 || data_addr == LLDB_INVALID_ADDRESS) return false; clang::ASTContext* ast = valobj.GetClangAST(); @@ -306,21 +315,21 @@ lldb_private::formatters::WCharStringSummaryProvider (ValueObject& valobj, Strea { case 8: // utf 8 - return ReadUTFBufferAndDumpToStream(valobj_addr, + return ReadUTFBufferAndDumpToStream(data_addr, process_sp, stream, 'L', true); // but use quotes case 16: // utf 16 - return ReadUTFBufferAndDumpToStream(valobj_addr, + return ReadUTFBufferAndDumpToStream(data_addr, process_sp, stream, 'L', true); // but use quotes case 32: // utf 32 - return ReadUTFBufferAndDumpToStream(valobj_addr, + return ReadUTFBufferAndDumpToStream(data_addr, process_sp, stream, 'L', @@ -332,6 +341,86 @@ lldb_private::formatters::WCharStringSummaryProvider (ValueObject& valobj, Strea return true; } +// this function extracts information from a libcxx std::basic_string<> +// irregardless of template arguments. it reports the size (in item count not bytes) +// and the location in memory where the string data can be found +static bool +ExtractLibcxxStringInfo (ValueObject& valobj, + ValueObjectSP &location_sp, + uint64_t& size) +{ + ValueObjectSP D(valobj.GetChildAtIndexPath({0,0,0,0})); + if (!D) + return false; + + ValueObjectSP size_mode(D->GetChildAtIndexPath({1,0,0})); + if (!size_mode) + return false; + + uint64_t size_mode_value(size_mode->GetValueAsUnsigned(0)); + + if ((size_mode_value & 1) == 0) // this means the string is in short-mode and the data is stored inline + { + ValueObjectSP s(D->GetChildAtIndex(1, true)); + if (!s) + return false; + size = ((size_mode_value >> 1) % 256); + location_sp = s->GetChildAtIndex(1, true); + return (location_sp.get() != nullptr); + } + else + { + ValueObjectSP l(D->GetChildAtIndex(0, true)); + if (!l) + return false; + location_sp = l->GetChildAtIndex(2, true); + ValueObjectSP size_vo(l->GetChildAtIndex(1, true)); + if (!size_vo || !location_sp) + return false; + size = size_vo->GetValueAsUnsigned(0); + return true; + } +} + +bool +lldb_private::formatters::LibcxxWStringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + uint64_t size = 0; + ValueObjectSP location_sp((ValueObject*)nullptr); + if (!ExtractLibcxxStringInfo(valobj, location_sp, size)) + return false; + if (size == 0) + { + stream.Printf("L\"\""); + return true; + } + if (!location_sp) + return false; + return WCharStringSummaryProvider(*location_sp.get(), stream); +} + +bool +lldb_private::formatters::LibcxxStringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + uint64_t size = 0; + ValueObjectSP location_sp((ValueObject*)nullptr); + if (!ExtractLibcxxStringInfo(valobj, location_sp, size)) + return false; + if (size == 0) + { + stream.Printf("\"\""); + return true; + } + if (!location_sp) + return false; + Error error; + location_sp->ReadPointedString(stream, + error, + 0, // max length is decided by the settings + false); // do not honor array (terminates on first 0 byte even for a char[]) + return error.Success(); +} + template bool lldb_private::formatters::NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream) diff --git a/lldb/source/Core/FormatManager.cpp b/lldb/source/Core/FormatManager.cpp index 184d65e1cf3d..144ddbff37c1 100644 --- a/lldb/source/Core/FormatManager.cpp +++ b/lldb/source/Core/FormatManager.cpp @@ -844,9 +844,12 @@ FormatManager::LoadLibcxxFormatters() .SetHideItemNames(false); #ifndef LLDB_DISABLE_PYTHON - std::string code(" lldb.formatters.cpp.libcxx.stdstring_SummaryProvider(valobj,internal_dict)"); - lldb::TypeSummaryImplSP std_string_summary_sp(new ScriptSummaryFormat(stl_summary_flags, "lldb.formatters.cpp.libcxx.stdstring_SummaryProvider",code.c_str())); + //std::string code(" lldb.formatters.cpp.libcxx.stdstring_SummaryProvider(valobj,internal_dict)"); + //lldb::TypeSummaryImplSP std_string_summary_sp(new ScriptSummaryFormat(stl_summary_flags, "lldb.formatters.cpp.libcxx.stdstring_SummaryProvider",code.c_str())); + lldb::TypeSummaryImplSP std_string_summary_sp(new CXXFunctionSummaryFormat(stl_summary_flags, lldb_private::formatters::LibcxxStringSummaryProvider, "std::string summary provider")); + lldb::TypeSummaryImplSP std_wstring_summary_sp(new CXXFunctionSummaryFormat(stl_summary_flags, lldb_private::formatters::LibcxxWStringSummaryProvider, "std::wstring summary provider")); + TypeCategoryImpl::SharedPointer libcxx_category_sp = GetCategory(m_libcxx_category_name); libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::string"), @@ -854,6 +857,11 @@ FormatManager::LoadLibcxxFormatters() libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::basic_string, std::__1::allocator >"), std_string_summary_sp); + libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::wstring"), + std_wstring_summary_sp); + libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::basic_string, std::__1::allocator >"), + std_wstring_summary_sp); + SyntheticChildren::Flags stl_synth_flags; stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(false); diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index 2a597b344c7c..449e49c0720a 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -533,6 +533,86 @@ ValueObject::GetChildAtIndex (uint32_t idx, bool can_create) return child_sp; } +ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::initializer_list& idxs, + uint32_t* index_of_error) +{ + if (idxs.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (uint32_t idx : idxs) + { + root = root->GetChildAtIndex(idx, true); + if (!root) + { + if (index_of_error) + *index_of_error = idx; + return root; + } + } + return root; +} + +ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::initializer_list< std::pair >& idxs, + uint32_t* index_of_error) +{ + if (idxs.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (std::pair idx : idxs) + { + root = root->GetChildAtIndex(idx.first, idx.second); + if (!root) + { + if (index_of_error) + *index_of_error = idx.first; + return root; + } + } + return root; +} + +lldb::ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::vector &idxs, + uint32_t* index_of_error) +{ + if (idxs.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (uint32_t idx : idxs) + { + root = root->GetChildAtIndex(idx, true); + if (!root) + { + if (index_of_error) + *index_of_error = idx; + return root; + } + } + return root; +} + +lldb::ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::vector< std::pair > &idxs, + uint32_t* index_of_error) +{ + if (idxs.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (std::pair idx : idxs) + { + root = root->GetChildAtIndex(idx.first, idx.second); + if (!root) + { + if (index_of_error) + *index_of_error = idx.first; + return root; + } + } + return root; +} + uint32_t ValueObject::GetIndexOfChildWithName (const ConstString &name) { diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/Makefile new file mode 100644 index 000000000000..f2f2b3f2fb82 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules + +CXXFLAGS += -stdlib=libc++ -O0 +LDFLAGS += -stdlib=libc++ \ No newline at end of file diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py new file mode 100644 index 000000000000..ca771e1c5b8e --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py @@ -0,0 +1,81 @@ +#coding=utf8 +""" +Test lldb data formatter subsystem. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class LibcxxStringDataFormatterTestCase(TestBase): + + mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "libcxx", "string") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dsym_test + def test_with_dsym_and_run_command(self): + """Test data formatter commands.""" + self.buildDsym() + self.data_formatter_commands() + + @skipOnLinux # No standard locations for libc++ on Linux, so skip for now + @dwarf_test + def test_with_dwarf_and_run_command(self): + """Test data formatter commands.""" + self.buildDwarf() + self.data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def data_formatter_commands(self): + """Test that that file and class static variables display correctly.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect("frame variable", + substrs = ['(std::__1::wstring) s = L"hello world! מזל טוב!"', + '(std::__1::wstring) S = L"!!!!"', + '(const wchar_t *) mazeltov = 0x','L"מזל טוב"', + '(std::__1::string) q = "hello world"', + '(std::__1::string) Q = "quite a long std::strin with lots of info inside it"']) + + self.runCmd("n") + + self.expect("frame variable", + substrs = ['(std::__1::wstring) s = L"hello world! מזל טוב!"', + '(std::__1::wstring) S = L"!!!!!"', + '(const wchar_t *) mazeltov = 0x','L"מזל טוב"', + '(std::__1::string) q = "hello world"', + '(std::__1::string) Q = "quite a long std::strin with lots of info inside it"']) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp new file mode 100644 index 000000000000..4a9b4fc7d0db --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp @@ -0,0 +1,12 @@ +#include + +int main() +{ + std::wstring s(L"hello world! מזל טוב!"); + std::wstring S(L"!!!!"); + const wchar_t *mazeltov = L"מזל טוב"; + std::string q("hello world"); + std::string Q("quite a long std::strin with lots of info inside it"); + S.assign(L"!!!!!"); // Set break point at this line. + return 0; +} \ No newline at end of file