forked from OSchip/llvm-project
<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:
parent
3dd236cdd8
commit
3309d88198
|
@ -49,6 +49,12 @@ namespace lldb_private {
|
||||||
bool
|
bool
|
||||||
WCharStringSummaryProvider (ValueObject& valobj, Stream& stream); // wchar_t*
|
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>
|
template<bool name_entries>
|
||||||
bool
|
bool
|
||||||
NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream);
|
NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream);
|
||||||
|
|
|
@ -734,6 +734,23 @@ public:
|
||||||
virtual lldb::ValueObjectSP
|
virtual lldb::ValueObjectSP
|
||||||
GetChildAtIndex (uint32_t idx, bool can_create);
|
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
|
virtual lldb::ValueObjectSP
|
||||||
GetChildMemberWithName (const ConstString &name, bool can_create);
|
GetChildMemberWithName (const ConstString &name, bool can_create);
|
||||||
|
|
||||||
|
|
|
@ -158,8 +158,9 @@ ReadUTFBufferAndDumpToStream (uint64_t location,
|
||||||
// if not UTF8, I need a conversion function to return proper UTF8
|
// if not UTF8, I need a conversion function to return proper UTF8
|
||||||
if (origin_encoding != 8 && !ConvertFunction)
|
if (origin_encoding != 8 && !ConvertFunction)
|
||||||
return false;
|
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;
|
Error error;
|
||||||
lldb::DataBufferSP buffer_sp(new DataBufferHeap(bufferSPSize,0));
|
lldb::DataBufferSP buffer_sp(new DataBufferHeap(bufferSPSize,0));
|
||||||
|
@ -218,6 +219,9 @@ ReadUTFBufferAndDumpToStream (uint64_t location,
|
||||||
utf8_data_end_ptr = (UTF8*)data_end_ptr;
|
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++)
|
for (;utf8_data_ptr != utf8_data_end_ptr; utf8_data_ptr++)
|
||||||
{
|
{
|
||||||
if (!*utf8_data_ptr)
|
if (!*utf8_data_ptr)
|
||||||
|
@ -290,9 +294,14 @@ lldb_private::formatters::WCharStringSummaryProvider (ValueObject& valobj, Strea
|
||||||
if (!process_sp)
|
if (!process_sp)
|
||||||
return false;
|
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;
|
return false;
|
||||||
|
|
||||||
clang::ASTContext* ast = valobj.GetClangAST();
|
clang::ASTContext* ast = valobj.GetClangAST();
|
||||||
|
@ -306,21 +315,21 @@ lldb_private::formatters::WCharStringSummaryProvider (ValueObject& valobj, Strea
|
||||||
{
|
{
|
||||||
case 8:
|
case 8:
|
||||||
// utf 8
|
// utf 8
|
||||||
return ReadUTFBufferAndDumpToStream<UTF8, nullptr>(valobj_addr,
|
return ReadUTFBufferAndDumpToStream<UTF8, nullptr>(data_addr,
|
||||||
process_sp,
|
process_sp,
|
||||||
stream,
|
stream,
|
||||||
'L',
|
'L',
|
||||||
true); // but use quotes
|
true); // but use quotes
|
||||||
case 16:
|
case 16:
|
||||||
// utf 16
|
// utf 16
|
||||||
return ReadUTFBufferAndDumpToStream<UTF16, ConvertUTF16toUTF8>(valobj_addr,
|
return ReadUTFBufferAndDumpToStream<UTF16, ConvertUTF16toUTF8>(data_addr,
|
||||||
process_sp,
|
process_sp,
|
||||||
stream,
|
stream,
|
||||||
'L',
|
'L',
|
||||||
true); // but use quotes
|
true); // but use quotes
|
||||||
case 32:
|
case 32:
|
||||||
// utf 32
|
// utf 32
|
||||||
return ReadUTFBufferAndDumpToStream<UTF32, ConvertUTF32toUTF8>(valobj_addr,
|
return ReadUTFBufferAndDumpToStream<UTF32, ConvertUTF32toUTF8>(data_addr,
|
||||||
process_sp,
|
process_sp,
|
||||||
stream,
|
stream,
|
||||||
'L',
|
'L',
|
||||||
|
@ -332,6 +341,86 @@ lldb_private::formatters::WCharStringSummaryProvider (ValueObject& valobj, Strea
|
||||||
return true;
|
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>
|
template<bool name_entries>
|
||||||
bool
|
bool
|
||||||
lldb_private::formatters::NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream)
|
lldb_private::formatters::NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream)
|
||||||
|
|
|
@ -844,9 +844,12 @@ FormatManager::LoadLibcxxFormatters()
|
||||||
.SetHideItemNames(false);
|
.SetHideItemNames(false);
|
||||||
|
|
||||||
#ifndef LLDB_DISABLE_PYTHON
|
#ifndef LLDB_DISABLE_PYTHON
|
||||||
std::string code(" lldb.formatters.cpp.libcxx.stdstring_SummaryProvider(valobj,internal_dict)");
|
//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 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);
|
TypeCategoryImpl::SharedPointer libcxx_category_sp = GetCategory(m_libcxx_category_name);
|
||||||
|
|
||||||
libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::string"),
|
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> >"),
|
libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >"),
|
||||||
std_string_summary_sp);
|
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;
|
SyntheticChildren::Flags stl_synth_flags;
|
||||||
stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(false);
|
stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(false);
|
||||||
|
|
||||||
|
|
|
@ -533,6 +533,86 @@ ValueObject::GetChildAtIndex (uint32_t idx, bool can_create)
|
||||||
return child_sp;
|
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
|
uint32_t
|
||||||
ValueObject::GetIndexOfChildWithName (const ConstString &name)
|
ValueObject::GetIndexOfChildWithName (const ConstString &name)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
LEVEL = ../../../../../make
|
||||||
|
|
||||||
|
CXX_SOURCES := main.cpp
|
||||||
|
|
||||||
|
include $(LEVEL)/Makefile.rules
|
||||||
|
|
||||||
|
CXXFLAGS += -stdlib=libc++ -O0
|
||||||
|
LDFLAGS += -stdlib=libc++
|
|
@ -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()
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue