New category "gnu-libstdc++" provides summary for std::string and synthetic children for types std::map, std::list and std::vector

The category is enabled by default. If you run into issues with it, disable it and the previous behavior of LLDB is restored
 ** This is a temporary solution. The general solution to having formatters pulled in at startup should involve going through the Platform.
Fixed an issue in type synthetic list where a category with synthetic providers in it was not shown if all the providers were regex-based

llvm-svn: 137850
This commit is contained in:
Enrico Granata 2011-08-17 19:07:52 +00:00
parent 23594f6b44
commit 217f91fc57
9 changed files with 338 additions and 16 deletions

View File

@ -270,6 +270,12 @@ public:
static void
SettingsTerminate ();
static void
FormatManagerInitialize();
static void
FormatManagerTerminate();
static void
Destroy (lldb::DebuggerSP &debugger_sp);

View File

@ -654,7 +654,6 @@
2669605E1199F4230075C61A /* lldb.swig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = lldb.swig; sourceTree = "<group>"; };
266960601199F4230075C61A /* build-swig-Python.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "build-swig-Python.sh"; sourceTree = "<group>"; };
266960611199F4230075C61A /* edit-swig-python-wrapper-file.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = "edit-swig-python-wrapper-file.py"; sourceTree = "<group>"; };
266960621199F4230075C61A /* finish-swig-Python-lldb.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "finish-swig-Python-lldb.sh"; sourceTree = "<group>"; };
266960631199F4230075C61A /* sed-sources */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "sed-sources"; sourceTree = "<group>"; };
266A42D5128E3FFB0090CF7C /* ClangNamespaceDecl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangNamespaceDecl.cpp; path = source/Symbol/ClangNamespaceDecl.cpp; sourceTree = "<group>"; };
266A42D7128E40040090CF7C /* ClangNamespaceDecl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangNamespaceDecl.h; path = include/lldb/Symbol/ClangNamespaceDecl.h; sourceTree = "<group>"; };
@ -1179,6 +1178,7 @@
94B6E76013D8833C005F417F /* ValueObjectSyntheticFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ValueObjectSyntheticFilter.h; path = include/lldb/Core/ValueObjectSyntheticFilter.h; sourceTree = "<group>"; };
94B6E76113D88362005F417F /* ValueObjectSyntheticFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectSyntheticFilter.cpp; path = source/Core/ValueObjectSyntheticFilter.cpp; sourceTree = "<group>"; };
94EBAC8313D9EE26009BA64E /* PythonPointer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PythonPointer.h; path = include/lldb/Utility/PythonPointer.h; sourceTree = "<group>"; };
94FE476613FC1DA8001F8475 /* finish-swig-Python-LLDB.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "finish-swig-Python-LLDB.sh"; sourceTree = "<group>"; };
961FABB81235DE1600F93A47 /* FuncUnwinders.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FuncUnwinders.cpp; path = source/Symbol/FuncUnwinders.cpp; sourceTree = "<group>"; };
961FABB91235DE1600F93A47 /* UnwindPlan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindPlan.cpp; path = source/Symbol/UnwindPlan.cpp; sourceTree = "<group>"; };
961FABBA1235DE1600F93A47 /* UnwindTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindTable.cpp; path = source/Symbol/UnwindTable.cpp; sourceTree = "<group>"; };
@ -1734,7 +1734,7 @@
children = (
266960601199F4230075C61A /* build-swig-Python.sh */,
266960611199F4230075C61A /* edit-swig-python-wrapper-file.py */,
266960621199F4230075C61A /* finish-swig-Python-lldb.sh */,
94FE476613FC1DA8001F8475 /* finish-swig-Python-LLDB.sh */,
9A48A3A7124AAA5A00922451 /* python-extensions.swig */,
94005E0313F438DF001EF42D /* python-wrapper.swig */,
);

View File

@ -165,5 +165,20 @@ else
fi
fi
# Copy the OSX C++ STL formatters over to the framework Python directory
if [ -f "${SRC_ROOT}/source/osxcpp.py" ]
then
if [ $Debug == 1 ]
then
echo "Copying osxcpp.py to ${framework_python_dir}"
fi
cp "${SRC_ROOT}/source/osxcpp.py" "${framework_python_dir}"
else
if [ $Debug == 1 ]
then
echo "Unable to find ${SRC_ROOT}/source/osxcpp.py"
fi
fi
exit 0

View File

@ -1538,7 +1538,7 @@ private:
CommandReturnObject* result = param->result;
// if the category is disabled or empty and there is no regex, just skip it
if ((cate->IsEnabled() == false || cate->GetCount() == 0) && param->cate_regex == NULL)
if ((cate->IsEnabled() == false || cate->GetCount(FormatCategory::eSummary | FormatCategory::eRegexSummary) == 0) && param->cate_regex == NULL)
return true;
// if we have a regex and this category does not match it, just skip it
@ -2032,7 +2032,7 @@ private:
CommandReturnObject* result = param->result;
// if the category is disabled or empty and there is no regex, just skip it
if ((cate->IsEnabled() == false || cate->Filter()->GetCount() == 0) && param->cate_regex == NULL)
if ((cate->IsEnabled() == false || cate->GetCount(FormatCategory::eFilter | FormatCategory::eRegexFilter) == 0) && param->cate_regex == NULL)
return true;
// if we have a regex and this category does not match it, just skip it
@ -2240,7 +2240,7 @@ private:
CommandReturnObject* result = param->result;
// if the category is disabled or empty and there is no regex, just skip it
if ((cate->IsEnabled() == false || cate->Synth()->GetCount() == 0) && param->cate_regex == NULL)
if ((cate->IsEnabled() == false || cate->GetCount(FormatCategory::eSynth | FormatCategory::eRegexSynth) == 0) && param->cate_regex == NULL)
return true;
// if we have a regex and this category does not match it, just skip it

View File

@ -82,6 +82,7 @@ Debugger::Initialize ()
if (g_shared_debugger_refcount == 0)
{
lldb_private::Initialize();
Debugger::FormatManagerInitialize();
}
g_shared_debugger_refcount++;
@ -95,6 +96,7 @@ Debugger::Terminate ()
g_shared_debugger_refcount--;
if (g_shared_debugger_refcount == 0)
{
Debugger::FormatManagerTerminate();
lldb_private::WillTerminate();
lldb_private::Terminate();
@ -1781,6 +1783,72 @@ GetFormatManager() {
return g_format_manager;
}
// The platform should be responsible for initializing its own formatters
// (e.g. to handle versioning, different runtime libraries, ...)
// Currently, basic formatters for std:: objects as implemented by
// the GNU libstdc++ are defined regardless, and enabled by default
// This is going to be moved to some platform-dependent location
// (in the meanwhile, these formatters should work for Mac OS X & Linux)
void
Debugger::FormatManagerInitialize()
{
static bool g_initialized = false;
if (!g_initialized)
{
g_initialized = true;
ConstString gnulib("gnu-libstdc++");
FormatManager& format_mgr = GetFormatManager();
lldb::FormatCategorySP osxcpp = format_mgr.Category(gnulib.AsCString());
osxcpp->Summary()->Add(ConstString("std::string").AsCString(),
SummaryFormatSP(new StringSummaryFormat(true,
false,
false,
true,
true,
false,
"${var._M_dataplus._M_p}")));
osxcpp->Summary()->Add(ConstString("std::basic_string<char>").AsCString(),
SummaryFormatSP(new StringSummaryFormat(true,
false,
false,
true,
true,
false,
"${var._M_dataplus._M_p}")));
osxcpp->Summary()->Add(ConstString("std::basic_string<char,std::char_traits<char>,std::allocator<char> >").AsCString(),
SummaryFormatSP(new StringSummaryFormat(true,
false,
false,
true,
true,
false,
"${var._M_dataplus._M_p}")));
osxcpp->RegexSynth()->Add(RegularExpressionSP(new RegularExpression("std::vector<")),
SyntheticChildrenSP(new SyntheticScriptProvider(true,
false,
false,
"StdVectorSynthProvider")));
osxcpp->RegexSynth()->Add(RegularExpressionSP(new RegularExpression("std::map<")),
SyntheticChildrenSP(new SyntheticScriptProvider(true,
false,
false,
"StdMapSynthProvider")));
osxcpp->RegexSynth()->Add(RegularExpressionSP(new RegularExpression("std::list<")),
SyntheticChildrenSP(new SyntheticScriptProvider(true,
false,
false,
"StdListSynthProvider")));
format_mgr.EnableCategory(gnulib.AsCString());
}
}
void
Debugger::FormatManagerTerminate()
{}
void
Debugger::Formatting::ForceUpdate()
{

View File

@ -173,6 +173,11 @@ ScriptInterpreterPython::ScriptInterpreterPython (CommandInterpreter &interprete
interpreter.GetDebugger().GetID());
PyRun_SimpleString (run_string.GetData());
run_string.Clear();
run_string.Printf ("run_one_line (%s, 'from osxcpp import *')", m_dictionary_name.c_str(),
interpreter.GetDebugger().GetID());
PyRun_SimpleString (run_string.GetData());
if (m_dbg_stdout != NULL)
{
m_new_sysout = PyFile_FromFile (m_dbg_stdout, (char *) "", (char *) "w", _check_and_flush);

View File

@ -656,10 +656,7 @@ AppleObjCRuntimeV2::GetActualTypeName(lldb_private::ObjCLanguageRuntime::ObjCISA
struct class_t *superclass;
Cache cache;
IMP *vtable;
--> uintptr_t data_NEVER_USE;
WARNING: this data_NEVER_USE pointer might one day contain flags in the least-significant bits
currently, rdar://problem/8955342 prevents the runtime from doing so
it presently is just a pointer to a class_rw_t
--> class_rw_t data;
*/
lldb::addr_t rw_pointer = isa + (4 * pointer_size);

231
lldb/source/osxcpp.py Normal file
View File

@ -0,0 +1,231 @@
import re
# C++ STL formatters for LLDB
# These formatters are based upon the version of the STL that ships
# with Mac OS X Snow Leopard 10.6.8 and OS X Lion 10.7.0
# The STL implementation *might* change on other releases of Apple's
# operating system and library infrastructure, and might be different on
# other operating systems
class StdListSynthProvider:
def __init__(self, valobj, dict):
self.valobj = valobj
self.update()
def num_children(self):
next_val = self.next.GetValueAsUnsigned(0)
prev_val = self.prev.GetValueAsUnsigned(0)
# After a std::list has been initialized, both next and prev will be non-NULL
if next_val == 0 or prev_val == 0:
return 0
if next_val == self.node_address:
return 0
if next_val == prev_val:
return 1
size = 2
current = self.next
while current.GetChildMemberWithName('_M_next').GetValueAsUnsigned(0) != self.node_address:
size = size + 1
current = current.GetChildMemberWithName('_M_next')
return (size - 1)
def get_child_index(self,name):
return int(name.lstrip('[').rstrip(']'))
def get_child_at_index(self,index):
if index >= self.num_children():
return None;
offset = index
current = self.next
while offset > 0:
current = current.GetChildMemberWithName('_M_next')
offset = offset - 1
return current.CreateChildAtOffset('['+str(index)+']',2*current.GetType().GetByteSize(),self.data_type)
def extract_type_name(self,name):
self.type_name = name[16:]
index = 2
count_of_template = 1
while index < len(self.type_name):
if self.type_name[index] == '<':
count_of_template = count_of_template + 1
elif self.type_name[index] == '>':
count_of_template = count_of_template - 1
elif self.type_name[index] == ',' and count_of_template == 1:
self.type_name = self.type_name[:index]
break
index = index + 1
self.type_name_nospaces = self.type_name.replace(", ", ",")
def update(self):
impl = self.valobj.GetChildMemberWithName('_M_impl')
node = impl.GetChildMemberWithName('_M_node')
self.extract_type_name(impl.GetType().GetName())
self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)
self.next = node.GetChildMemberWithName('_M_next')
self.prev = node.GetChildMemberWithName('_M_prev')
self.data_type = node.GetTarget().FindFirstType(self.type_name)
# tries to fight against a difference in formatting type names between gcc and clang
if self.data_type.IsValid() == False:
self.data_type = node.GetTarget().FindFirstType(self.type_name_nospaces)
self.data_size = self.data_type.GetByteSize()
class StdVectorSynthProvider:
def __init__(self, valobj, dict):
self.valobj = valobj;
self.update()
def num_children(self):
start_val = self.start.GetValueAsUnsigned(0)
finish_val = self.finish.GetValueAsUnsigned(0)
end_val = self.end.GetValueAsUnsigned(0)
# Before a vector has been constructed, it will contain bad values
# so we really need to be careful about the length we return since
# unitialized data can cause us to return a huge number. We need
# to also check for any of the start, finish or end of storage values
# being zero (NULL). If any are, then this vector has not been
# initialized yet and we should return zero
# Make sure nothing is NULL
if start_val == 0 or finish_val == 0 or end_val == 0:
return 0
# Make sure start is less than finish
if start_val >= finish_val:
return 0
# Make sure finish is less than or equal to end of storage
if finish_val > end_val:
return 0
num_children = (finish_val-start_val)/self.data_size
return num_children
def get_child_index(self,name):
return int(name.lstrip('[').rstrip(']'))
def get_child_at_index(self,index):
if index >= self.num_children():
return None;
offset = index * self.data_size
return self.start.CreateChildAtOffset('['+str(index)+']',offset,self.data_type)
def update(self):
impl = self.valobj.GetChildMemberWithName('_M_impl')
self.start = impl.GetChildMemberWithName('_M_start')
self.finish = impl.GetChildMemberWithName('_M_finish')
self.end = impl.GetChildMemberWithName('_M_end_of_storage')
self.data_type = self.start.GetType().GetPointeeType()
self.data_size = self.data_type.GetByteSize()
class StdMapSynthProvider:
def __init__(self, valobj, dict):
self.valobj = valobj;
self.update()
def update(self):
self.Mt = self.valobj.GetChildMemberWithName('_M_t')
self.Mimpl = self.Mt.GetChildMemberWithName('_M_impl')
self.Mheader = self.Mimpl.GetChildMemberWithName('_M_header')
# from libstdc++ implementation of _M_root for rbtree
self.Mroot = self.Mheader.GetChildMemberWithName('_M_parent')
# the stuff into the tree is actually a std::pair<const key, value>
# life would be much easier if gcc had a coherent way to print out
# template names in debug info
self.expand_clang_type_name()
self.expand_gcc_type_name()
self.data_type = self.Mt.GetTarget().FindFirstType(self.clang_type_name)
if self.data_type.IsValid() == False:
self.data_type = self.Mt.GetTarget().FindFirstType(self.gcc_type_name)
self.data_size = self.data_type.GetByteSize()
self.skip_size = self.Mheader.GetType().GetByteSize()
def expand_clang_type_name(self):
type_name = self.Mimpl.GetType().GetName()
index = type_name.find("std::pair<")
type_name = type_name[index+5:]
index = 6
template_count = 1
while index < len(type_name):
if type_name[index] == '<':
template_count = template_count + 1
elif type_name[index] == '>' and template_count == 1:
type_name = type_name[:index+1]
break
elif type_name[index] == '>':
template_count = template_count - 1
index = index + 1;
self.clang_type_name = type_name
def expand_gcc_type_name(self):
type_name = self.Mt.GetType().GetName()
index = type_name.find("std::pair<")
type_name = type_name[index+5:]
index = 6
template_count = 1
while index < len(type_name):
if type_name[index] == '<':
template_count = template_count + 1
elif type_name[index] == '>' and template_count == 1:
type_name = type_name[:index+1]
break
elif type_name[index] == '>':
template_count = template_count - 1
elif type_name[index] == ' ' and template_count == 1 and type_name[index-1] == ',':
type_name = type_name[0:index] + type_name[index+1:]
index = index - 1
index = index + 1;
self.gcc_type_name = type_name
def num_children(self):
root_ptr_val = self.node_ptr_value(self.Mroot)
if root_ptr_val == 0:
return 0;
return self.Mimpl.GetChildMemberWithName('_M_node_count').GetValueAsUnsigned(0)
def get_child_index(self,name):
return int(name.lstrip('[').rstrip(']'))
def get_child_at_index(self,index):
if index >= self.num_children():
return None;
offset = index
current = self.left(self.Mheader);
while offset > 0:
current = self.increment_node(current)
offset = offset - 1;
# skip all the base stuff and get at the data
return current.CreateChildAtOffset('['+str(index)+']',self.skip_size,self.data_type)
# utility functions
def node_ptr_value(self,node):
return node.GetValueAsUnsigned(0)
def right(self,node):
return node.GetChildMemberWithName("_M_right");
def left(self,node):
return node.GetChildMemberWithName("_M_left");
def parent(self,node):
return node.GetChildMemberWithName("_M_parent");
# from libstdc++ implementation of iterator for rbtree
def increment_node(self,node):
if self.node_ptr_value(self.right(node)) != 0:
x = self.right(node);
while self.node_ptr_value(self.left(x)) != 0:
x = self.left(x);
return x;
else:
x = node;
y = self.parent(x)
while(self.node_ptr_value(x) == self.node_ptr_value(self.right(y))):
x = y;
y = self.parent(y);
if self.node_ptr_value(self.right(x)) != self.node_ptr_value(y):
x = y;
return x;

View File

@ -222,9 +222,9 @@ class DataFormatterTestCase(TestBase):
# std::vector
self.runCmd("script from StdVectorSynthProvider import *")
self.runCmd("type synth add -l StdVectorSynthProvider std::int_vect int_vect")
self.runCmd("type synth add -l StdVectorSynthProvider std::string_vect string_vect")
#self.runCmd("script from StdVectorSynthProvider import *")
#self.runCmd("type synth add -l StdVectorSynthProvider std::int_vect int_vect")
#self.runCmd("type synth add -l StdVectorSynthProvider std::string_vect string_vect")
self.runCmd("n")
@ -351,12 +351,12 @@ class DataFormatterTestCase(TestBase):
substrs = ['vector has 0 items'])
# now test std::list
self.runCmd("script from StdListSynthProvider import *")
#self.runCmd("script from StdListSynthProvider import *")
self.runCmd("n")
self.runCmd("frame variable numbers_list -T")
self.runCmd("type synth add std::int_list std::string_list int_list string_list -l StdListSynthProvider")
#self.runCmd("type synth add std::int_list std::string_list int_list string_list -l StdListSynthProvider")
self.runCmd("type summary add std::int_list std::string_list int_list string_list -f \"list has ${svar%#} items\" -e")
self.runCmd("type format add -f hex int")
@ -467,13 +467,13 @@ class DataFormatterTestCase(TestBase):
self.runCmd("n")
self.runCmd("frame variable ii -T")
self.runCmd("script from StdMapSynthProvider import *")
#self.runCmd("script from StdMapSynthProvider import *")
self.runCmd("type summary add -x \"std::map<\" -f \"map has ${svar%#} items\" -e")
#import time
#time.sleep(30)
self.runCmd("type synth add -x \"std::map<\" -l StdMapSynthProvider")
#self.runCmd("type synth add -x \"std::map<\" -l StdMapSynthProvider")
self.expect('frame variable ii',