<rdar://problem/12239827>

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
This commit is contained in:
Enrico Granata 2013-01-12 01:00:22 +00:00
parent 3dd236cdd8
commit 3309d88198
8 changed files with 310 additions and 9 deletions

View File

@ -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 name_entries>
bool
NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream);

View File

@ -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<uint32_t> &idxs,
uint32_t* index_of_error = NULL);
lldb::ValueObjectSP
GetChildAtIndexPath (const std::vector<uint32_t> &idxs,
uint32_t* index_of_error = NULL);
lldb::ValueObjectSP
GetChildAtIndexPath (const std::initializer_list< std::pair<uint32_t, bool> > &idxs,
uint32_t* index_of_error = NULL);
lldb::ValueObjectSP
GetChildAtIndexPath (const std::vector< std::pair<uint32_t, bool> > &idxs,
uint32_t* index_of_error = NULL);
virtual lldb::ValueObjectSP
GetChildMemberWithName (const ConstString &name, bool can_create);

View File

@ -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<UTF8, nullptr>(valobj_addr,
return ReadUTFBufferAndDumpToStream<UTF8, nullptr>(data_addr,
process_sp,
stream,
'L',
true); // but use quotes
case 16:
// utf 16
return ReadUTFBufferAndDumpToStream<UTF16, ConvertUTF16toUTF8>(valobj_addr,
return ReadUTFBufferAndDumpToStream<UTF16, ConvertUTF16toUTF8>(data_addr,
process_sp,
stream,
'L',
true); // but use quotes
case 32:
// utf 32
return ReadUTFBufferAndDumpToStream<UTF32, ConvertUTF32toUTF8>(valobj_addr,
return ReadUTFBufferAndDumpToStream<UTF32, ConvertUTF32toUTF8>(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 name_entries>
bool
lldb_private::formatters::NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream)

View File

@ -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<char, std::__1::char_traits<char>, std::__1::allocator<char> >"),
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<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> >"),
std_wstring_summary_sp);
SyntheticChildren::Flags stl_synth_flags;
stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(false);

View File

@ -533,6 +533,86 @@ ValueObject::GetChildAtIndex (uint32_t idx, bool can_create)
return child_sp;
}
ValueObjectSP
ValueObject::GetChildAtIndexPath (const std::initializer_list<uint32_t>& 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<uint32_t, bool> >& idxs,
uint32_t* index_of_error)
{
if (idxs.size() == 0)
return GetSP();
ValueObjectSP root(GetSP());
for (std::pair<uint32_t, bool> 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<uint32_t> &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<uint32_t, bool> > &idxs,
uint32_t* index_of_error)
{
if (idxs.size() == 0)
return GetSP();
ValueObjectSP root(GetSP());
for (std::pair<uint32_t, bool> 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)
{

View File

@ -0,0 +1,8 @@
LEVEL = ../../../../../make
CXX_SOURCES := main.cpp
include $(LEVEL)/Makefile.rules
CXXFLAGS += -stdlib=libc++ -O0
LDFLAGS += -stdlib=libc++

View File

@ -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()

View File

@ -0,0 +1,12 @@
#include <string>
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;
}