<rdar://problem/12953018>

Synthetic children and summary for std::vector<bool> (for both libcxx and libstdcpp).
std::vector<bool> is a special case and is custom-implemented to be a vector of bits, which means we failed to handle it with the standard std::vector<T> formatter.
This checkin provides custom formatters that work correctly

llvm-svn: 174333
This commit is contained in:
Enrico Granata
2013-02-04 22:54:42 +00:00
parent d6cef10af8
commit 3b1b2dabda
9 changed files with 675 additions and 1 deletions

View File

@@ -15,6 +15,7 @@
#include "lldb/Core/ConstString.h"
#include "lldb/DataFormatters/FormatClasses.h"
#include "lldb/Target/Target.h"
#include "clang/AST/ASTContext.h"
@@ -348,6 +349,69 @@ namespace lldb_private {
SyntheticChildrenFrontEnd* NSDictionarySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP);
class LibcxxVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd
{
public:
LibcxxVectorBoolSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp);
virtual size_t
CalculateNumChildren ();
virtual lldb::ValueObjectSP
GetChildAtIndex (size_t idx);
virtual bool
Update();
virtual bool
MightHaveChildren ();
virtual size_t
GetIndexOfChildWithName (const ConstString &name);
virtual
~LibcxxVectorBoolSyntheticFrontEnd ();
private:
ExecutionContextRef m_exe_ctx_ref;
uint64_t m_count;
lldb::addr_t m_base_data_address;
EvaluateExpressionOptions m_options;
};
SyntheticChildrenFrontEnd* LibcxxVectorBoolSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP);
class LibstdcppVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd
{
public:
LibstdcppVectorBoolSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp);
virtual size_t
CalculateNumChildren ();
virtual lldb::ValueObjectSP
GetChildAtIndex (size_t idx);
virtual bool
Update();
virtual bool
MightHaveChildren ();
virtual size_t
GetIndexOfChildWithName (const ConstString &name);
virtual
~LibstdcppVectorBoolSyntheticFrontEnd ();
private:
ExecutionContextRef m_exe_ctx_ref;
uint64_t m_count;
lldb::addr_t m_base_data_address;
EvaluateExpressionOptions m_options;
};
SyntheticChildrenFrontEnd* LibstdcppVectorBoolSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP);
}
}

View File

@@ -1787,6 +1787,322 @@ lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::GetChildAtIndex (size_
return dict_item.valobj_sp;
}
lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::LibcxxVectorBoolSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
SyntheticChildrenFrontEnd(*valobj_sp.get()),
m_exe_ctx_ref(),
m_count(0),
m_base_data_address(0),
m_options()
{
if (valobj_sp)
Update();
m_options.SetCoerceToId(false)
.SetUnwindOnError(true)
.SetKeepInMemory(true)
.SetUseDynamic(lldb::eDynamicCanRunTarget);
}
size_t
lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::CalculateNumChildren ()
{
return m_count;
}
lldb::ValueObjectSP
lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetChildAtIndex (size_t idx)
{
if (idx >= m_count)
return ValueObjectSP();
if (m_base_data_address == 0 || m_count == 0)
return ValueObjectSP();
size_t byte_idx = (idx >> 3); // divide by 8 to get byte index
size_t bit_index = (idx & 7); // efficient idx % 8 for bit index
lldb::addr_t byte_location = m_base_data_address + byte_idx;
ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
if (!process_sp)
return ValueObjectSP();
uint8_t byte = 0;
uint8_t mask = 0;
Error err;
size_t bytes_read = process_sp->ReadMemory(byte_location, &byte, 1, err);
if (err.Fail() || bytes_read == 0)
return ValueObjectSP();
switch (bit_index)
{
case 0:
mask = 1; break;
case 1:
mask = 2; break;
case 2:
mask = 4; break;
case 3:
mask = 8; break;
case 4:
mask = 16; break;
case 5:
mask = 32; break;
case 6:
mask = 64; break;
case 7:
mask = 128; break;
default:
return ValueObjectSP();
}
bool bit_set = ((byte & mask) != 0);
Target& target(process_sp->GetTarget());
ValueObjectSP retval_sp;
if (bit_set)
target.EvaluateExpression("(bool)true", NULL, retval_sp);
else
target.EvaluateExpression("(bool)false", NULL, retval_sp);
StreamString name; name.Printf("[%zu]",idx);
if (retval_sp)
retval_sp->SetName(ConstString(name.GetData()));
return retval_sp;
}
/*(std::__1::vector<std::__1::allocator<bool> >) vBool = {
__begin_ = 0x00000001001000e0
__size_ = 56
__cap_alloc_ = {
std::__1::__libcpp_compressed_pair_imp<unsigned long, std::__1::allocator<unsigned long> > = {
__first_ = 1
}
}
}*/
bool
lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::Update()
{
ValueObjectSP valobj_sp = m_backend.GetSP();
if (!valobj_sp)
return false;
if (valobj_sp->IsDynamic())
valobj_sp = valobj_sp->GetStaticValue();
if (!valobj_sp)
return false;
m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
ValueObjectSP size_sp(valobj_sp->GetChildMemberWithName(ConstString("__size_"), true));
if (!size_sp)
return false;
m_count = size_sp->GetValueAsUnsigned(0);
if (!m_count)
return true;
ValueObjectSP begin_sp(valobj_sp->GetChildMemberWithName(ConstString("__begin_"), true));
if (!begin_sp)
{
m_count = 0;
return false;
}
m_base_data_address = begin_sp->GetValueAsUnsigned(0);
if (!m_base_data_address)
{
m_count = 0;
return false;
}
return true;
}
bool
lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::MightHaveChildren ()
{
return true;
}
size_t
lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name)
{
if (!m_count || !m_base_data_address)
return UINT32_MAX;
const char* item_name = name.GetCString();
uint32_t idx = ExtractIndexFromString(item_name);
if (idx < UINT32_MAX && idx >= CalculateNumChildren())
return UINT32_MAX;
return idx;
}
lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::~LibcxxVectorBoolSyntheticFrontEnd ()
{}
SyntheticChildrenFrontEnd*
lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp)
{
if (!valobj_sp)
return NULL;
return (new LibcxxVectorBoolSyntheticFrontEnd(valobj_sp));
}
lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::LibstdcppVectorBoolSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
SyntheticChildrenFrontEnd(*valobj_sp.get()),
m_exe_ctx_ref(),
m_count(0),
m_base_data_address(0),
m_options()
{
if (valobj_sp)
Update();
m_options.SetCoerceToId(false)
.SetUnwindOnError(true)
.SetKeepInMemory(true)
.SetUseDynamic(lldb::eDynamicCanRunTarget);
}
size_t
lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::CalculateNumChildren ()
{
return m_count;
}
lldb::ValueObjectSP
lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::GetChildAtIndex (size_t idx)
{
if (idx >= m_count)
return ValueObjectSP();
if (m_base_data_address == 0 || m_count == 0)
return ValueObjectSP();
size_t byte_idx = (idx >> 3); // divide by 8 to get byte index
size_t bit_index = (idx & 7); // efficient idx % 8 for bit index
lldb::addr_t byte_location = m_base_data_address + byte_idx;
ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
if (!process_sp)
return ValueObjectSP();
uint8_t byte = 0;
uint8_t mask = 0;
Error err;
size_t bytes_read = process_sp->ReadMemory(byte_location, &byte, 1, err);
if (err.Fail() || bytes_read == 0)
return ValueObjectSP();
switch (bit_index)
{
case 0:
mask = 1; break;
case 1:
mask = 2; break;
case 2:
mask = 4; break;
case 3:
mask = 8; break;
case 4:
mask = 16; break;
case 5:
mask = 32; break;
case 6:
mask = 64; break;
case 7:
mask = 128; break;
default:
return ValueObjectSP();
}
bool bit_set = ((byte & mask) != 0);
Target& target(process_sp->GetTarget());
ValueObjectSP retval_sp;
if (bit_set)
target.EvaluateExpression("(bool)true", NULL, retval_sp);
else
target.EvaluateExpression("(bool)false", NULL, retval_sp);
StreamString name; name.Printf("[%zu]",idx);
if (retval_sp)
retval_sp->SetName(ConstString(name.GetData()));
return retval_sp;
}
/*((std::vector<std::allocator<bool> >) vBool = {
(std::_Bvector_base<std::allocator<bool> >) std::_Bvector_base<std::allocator<bool> > = {
(std::_Bvector_base<std::allocator<bool> >::_Bvector_impl) _M_impl = {
(std::_Bit_iterator) _M_start = {
(std::_Bit_iterator_base) std::_Bit_iterator_base = {
(_Bit_type *) _M_p = 0x0016b160
(unsigned int) _M_offset = 0
}
}
(std::_Bit_iterator) _M_finish = {
(std::_Bit_iterator_base) std::_Bit_iterator_base = {
(_Bit_type *) _M_p = 0x0016b16c
(unsigned int) _M_offset = 16
}
}
(_Bit_type *) _M_end_of_storage = 0x0016b170
}
}
}
*/
bool
lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::Update()
{
ValueObjectSP valobj_sp = m_backend.GetSP();
if (!valobj_sp)
return false;
if (valobj_sp->IsDynamic())
valobj_sp = valobj_sp->GetStaticValue();
if (!valobj_sp)
return false;
m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
ValueObjectSP m_impl_sp(valobj_sp->GetChildMemberWithName(ConstString("_M_impl"), true));
if (!m_impl_sp)
return false;
ValueObjectSP m_start_sp(m_impl_sp->GetChildMemberWithName(ConstString("_M_start"), true));
ValueObjectSP m_finish_sp(m_impl_sp->GetChildMemberWithName(ConstString("_M_finish"), true));
ValueObjectSP start_p_sp, finish_p_sp, finish_offset_sp;
if (!m_start_sp || !m_finish_sp)
return false;
start_p_sp = m_start_sp->GetChildMemberWithName(ConstString("_M_p"), true);
finish_p_sp = m_finish_sp->GetChildMemberWithName(ConstString("_M_p"), true);
finish_offset_sp = m_finish_sp->GetChildMemberWithName(ConstString("_M_offset"), true);
if (!start_p_sp || !finish_offset_sp || !finish_p_sp)
return false;
m_base_data_address = start_p_sp->GetValueAsUnsigned(0);
if (!m_base_data_address)
return false;
lldb::addr_t end_data_address(finish_p_sp->GetValueAsUnsigned(0));
if (!end_data_address)
return false;
if (end_data_address < m_base_data_address)
return false;
else
m_count = finish_offset_sp->GetValueAsUnsigned(0) + (end_data_address-m_base_data_address)*8;
return true;
}
bool
lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::MightHaveChildren ()
{
return true;
}
size_t
lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name)
{
if (!m_count || !m_base_data_address)
return UINT32_MAX;
const char* item_name = name.GetCString();
uint32_t idx = ExtractIndexFromString(item_name);
if (idx < UINT32_MAX && idx >= CalculateNumChildren())
return UINT32_MAX;
return idx;
}
lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::~LibstdcppVectorBoolSyntheticFrontEnd ()
{}
SyntheticChildrenFrontEnd*
lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp)
{
if (!valobj_sp)
return NULL;
return (new LibstdcppVectorBoolSyntheticFrontEnd(valobj_sp));
}
template bool
lldb_private::formatters::NSDictionarySummaryProvider<true> (ValueObject&, Stream&) ;

View File

@@ -561,6 +561,13 @@ FormatManager::LoadLibStdcppFormatters()
gnu_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::list<.+>(( )?&)?$")),
TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags,
"size=${svar%#}")));
gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::vector<std::allocator<bool> >"),
TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
gnu_category_sp->GetSyntheticNavigator()->Add(ConstString("std::vector<std::allocator<bool> >"),
SyntheticChildrenSP(new CXXSyntheticChildren(stl_synth_flags,"libc++ std::vector<bool> synthetic children",lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEndCreator)));
#endif
}
@@ -633,7 +640,10 @@ FormatManager::LoadLibcxxFormatters()
// this summary prevails on the regex std::vector<> because we do exact matches before regex ones
libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::vector<std::__1::allocator<bool> >"),
TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${var.__size_}")));
TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
libcxx_category_sp->GetSyntheticNavigator()->Add(ConstString("std::__1::vector<std::__1::allocator<bool> >"),
SyntheticChildrenSP(new CXXSyntheticChildren(stl_synth_flags,"libc++ std::vector<bool> synthetic children",lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEndCreator)));
#endif
}

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,70 @@
"""
Test lldb data formatter subsystem.
"""
import os, time
import unittest2
import lldb
from lldbtest import *
import lldbutil
class LibcxxVBoolDataFormatterTestCase(TestBase):
mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "libcxx", "vbool")
@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 vBool",
substrs = ['size=49','[0] = false','[1] = true','[18] = false','[27] = true','[36] = false','[47] = true','[48] = true'])
self.expect("expr vBool",
substrs = ['size=49','[0] = false','[1] = true','[18] = false','[27] = true','[36] = false','[47] = true','[48] = true'])
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@@ -0,0 +1,69 @@
#include <string>
#ifdef _LIBCPP_INLINE_VISIBILITY
#undef _LIBCPP_INLINE_VISIBILITY
#endif
#define _LIBCPP_INLINE_VISIBILITY
#include <vector>
int main()
{
std::vector<bool> vBool;
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(true);
return 0; // Set break point at this line.
}

View File

@@ -0,0 +1,5 @@
LEVEL = ../../../../../make
CXX_SOURCES := main.cpp
include $(LEVEL)/Makefile.rules

View File

@@ -0,0 +1,69 @@
"""
Test lldb data formatter subsystem.
"""
import os, time
import unittest2
import lldb
from lldbtest import *
import lldbutil
class StdVBoolDataFormatterTestCase(TestBase):
mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "libstdcpp", "vbool")
@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()
@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 vBool",
substrs = ['size=49','[0] = false','[1] = true','[18] = false','[27] = true','[36] = false','[47] = true','[48] = true'])
self.expect("expr vBool",
substrs = ['size=49','[0] = false','[1] = true','[18] = false','[27] = true','[36] = false','[47] = true','[48] = true'])
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@@ -0,0 +1,63 @@
#include <vector>
int main()
{
std::vector<bool> vBool;
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(false);
vBool.push_back(true);
vBool.push_back(true);
return 0; // Set break point at this line.
}