Please use GitHub pull requests for new patches. Avoid migrating existing patches. Phabricator shutdown timeline
Changeset View
Changeset View
Standalone View
Standalone View
lldb/examples/synthetic/gnu_libstdcpp.py
from __future__ import division | from __future__ import division | ||||
import re | import re | ||||
import lldb.formatters.Logger | import lldb.formatters.Logger | ||||
# C++ STL formatters for LLDB | # C++ STL formatters for LLDB | ||||
# These formatters are based upon the version of the GNU libstdc++ | # These formatters are based upon the version of the GNU libstdc++ | ||||
# as it ships with Mac OS X 10.6.8 thru 10.8.0 | # as it ships with Mac OS X 10.6.8 thru 10.8.0 | ||||
# You are encouraged to look at the STL implementation for your platform | # You are encouraged to look at the STL implementation for your platform | ||||
# before relying on these formatters to do the right thing for your setup | # before relying on these formatters to do the right thing for your setup | ||||
def StdOptionalSummaryProvider(valobj, dict): | |||||
has_value = valobj.GetNumChildren() > 0 | |||||
# We add wrapping spaces for consistency with the libcxx formatter | |||||
return " Has Value=" + ("true" if has_value else "false") + " " | |||||
class StdOptionalSynthProvider: | |||||
def __init__(self, valobj, dict): | |||||
self.valobj = valobj | |||||
def update(self): | |||||
try: | |||||
self.payload = self.valobj.GetChildMemberWithName('_M_payload') | |||||
self.value = self.payload.GetChildMemberWithName('_M_payload') | |||||
self.count = self.payload.GetChildMemberWithName('_M_engaged').GetValueAsUnsigned(0) | |||||
labath: I assume this is relying on the fact that `_M_engaged` can only contain values 1 and 0. While… | |||||
except: | |||||
self.count = 0 | |||||
return False | |||||
def num_children(self): | |||||
return self.count | |||||
def get_child_index(self, name): | |||||
return 0 | |||||
def get_child_at_index(self, index): | |||||
return self.value.Clone('Value') | |||||
""" | """ | ||||
This formatter can be applied to all | This formatter can be applied to all | ||||
unordered map-like structures (unordered_map, unordered_multimap, unordered_set, unordered_multiset) | unordered map-like structures (unordered_map, unordered_multimap, unordered_set, unordered_multiset) | ||||
""" | """ | ||||
class StdUnorderedMapSynthProvider: | class StdUnorderedMapSynthProvider: | ||||
def __init__(self, valobj, dict): | def __init__(self, valobj, dict): | ||||
self.valobj = valobj | self.valobj = valobj | ||||
self.count = None | self.count = None | ||||
self.kind = self.get_object_kind(valobj) | self.kind = self.get_object_kind(valobj) | ||||
def get_object_kind(self, valobj): | def get_object_kind(self, valobj): | ||||
type_name = valobj.GetTypeName() | type_name = valobj.GetTypeName() | ||||
return "set" if "set" in type_name else "map" | return "set" if "set" in type_name else "map" | ||||
def extract_type(self): | def extract_type(self): | ||||
type = self.valobj.GetType() | type = self.valobj.GetType() | ||||
# type of std::pair<key, value> is the first template | # type of std::pair<key, value> is the first template | ||||
# argument type of the 4th template argument to std::map and | # argument type of the 4th template argument to std::map and | ||||
# 3rd template argument for std::set. That's why | # 3rd template argument for std::set. That's why | ||||
# we need to know kind of the object | # we need to know kind of the object | ||||
template_arg_num = 4 if self.kind == "map" else 3 | template_arg_num = 4 if self.kind == "map" else 3 | ||||
allocator_type = type.GetTemplateArgumentType(template_arg_num) | allocator_type = type.GetTemplateArgumentType(template_arg_num) | ||||
data_type = allocator_type.GetTemplateArgumentType(0) | data_type = allocator_type.GetTemplateArgumentType(0) | ||||
return data_type | return data_type | ||||
def update(self): | def update(self): | ||||
# preemptively setting this to None - we might end up changing our mind | # preemptively setting this to None - we might end up changing our mind | ||||
# later | # later | ||||
self.count = None | self.count = None | ||||
try: | try: | ||||
self.head = self.valobj.GetChildMemberWithName('_M_h') | self.head = self.valobj.GetChildMemberWithName('_M_h') | ||||
self.before_begin = self.head.GetChildMemberWithName('_M_before_begin') | self.before_begin = self.head.GetChildMemberWithName('_M_before_begin') | ||||
self.next = self.before_begin.GetChildMemberWithName('_M_nxt') | self.next = self.before_begin.GetChildMemberWithName('_M_nxt') | ||||
self.data_type = self.extract_type() | self.data_type = self.extract_type() | ||||
Show All 13 Lines | def get_child_at_index(self, index): | ||||
logger = lldb.formatters.Logger.Logger() | logger = lldb.formatters.Logger.Logger() | ||||
logger >> "Being asked to fetch child[" + str(index) + "]" | logger >> "Being asked to fetch child[" + str(index) + "]" | ||||
if index < 0: | if index < 0: | ||||
return None | return None | ||||
if index >= self.num_children(): | if index >= self.num_children(): | ||||
return None | return None | ||||
try: | try: | ||||
offset = index | offset = index | ||||
current = self.next | current = self.next | ||||
while offset > 0: | while offset > 0: | ||||
current = current.GetChildMemberWithName('_M_nxt') | current = current.GetChildMemberWithName('_M_nxt') | ||||
offset = offset - 1 | offset = offset - 1 | ||||
return current.CreateChildAtOffset( '[' + str(index) + ']', self.skip_size, self.data_type) | return current.CreateChildAtOffset( '[' + str(index) + ']', self.skip_size, self.data_type) | ||||
except: | except: | ||||
logger >> "Cannot get child" | logger >> "Cannot get child" | ||||
return None | return None | ||||
def num_children(self): | def num_children(self): | ||||
if self.count is None: | if self.count is None: | ||||
self.count = self.num_children_impl() | self.count = self.num_children_impl() | ||||
return self.count | return self.count | ||||
Show All 29 Lines | class AbstractListSynthProvider: | ||||
def is_valid(self, node): | def is_valid(self, node): | ||||
logger = lldb.formatters.Logger.Logger() | logger = lldb.formatters.Logger.Logger() | ||||
valid = self.value(self.next_node(node)) != self.get_end_of_list_address() | valid = self.value(self.next_node(node)) != self.get_end_of_list_address() | ||||
if valid: | if valid: | ||||
logger >> "%s is valid" % str(self.valobj.GetName()) | logger >> "%s is valid" % str(self.valobj.GetName()) | ||||
else: | else: | ||||
logger >> "synthetic value is not valid" | logger >> "synthetic value is not valid" | ||||
return valid | return valid | ||||
def value(self, node): | def value(self, node): | ||||
logger = lldb.formatters.Logger.Logger() | logger = lldb.formatters.Logger.Logger() | ||||
value = node.GetValueAsUnsigned() | value = node.GetValueAsUnsigned() | ||||
logger >> "synthetic value for {}: {}".format( | logger >> "synthetic value for {}: {}".format( | ||||
str(self.valobj.GetName()), value) | str(self.valobj.GetName()), value) | ||||
return value | return value | ||||
# Floyd's cycle-finding algorithm | # Floyd's cycle-finding algorithm | ||||
Show All 29 Lines | def num_children(self): | ||||
return self.count | return self.count | ||||
def num_children_impl(self): | def num_children_impl(self): | ||||
logger = lldb.formatters.Logger.Logger() | logger = lldb.formatters.Logger.Logger() | ||||
try: | try: | ||||
# After a std::list has been initialized, both next and prev will | # After a std::list has been initialized, both next and prev will | ||||
# be non-NULL | # be non-NULL | ||||
next_val = self.next.GetValueAsUnsigned(0) | next_val = self.next.GetValueAsUnsigned(0) | ||||
if next_val == 0: | if next_val == 0: | ||||
return 0 | return 0 | ||||
if self.has_loop(): | if self.has_loop(): | ||||
return 0 | return 0 | ||||
if self.has_prev: | if self.has_prev: | ||||
prev_val = self.prev.GetValueAsUnsigned(0) | prev_val = self.prev.GetValueAsUnsigned(0) | ||||
if prev_val == 0: | if prev_val == 0: | ||||
return 0 | return 0 | ||||
if next_val == self.node_address: | if next_val == self.node_address: | ||||
return 0 | return 0 | ||||
if next_val == prev_val: | if next_val == prev_val: | ||||
return 1 | return 1 | ||||
size = 1 | size = 1 | ||||
current = self.next | current = self.next | ||||
while current.GetChildMemberWithName( | while current.GetChildMemberWithName( | ||||
'_M_next').GetValueAsUnsigned(0) != self.get_end_of_list_address(): | '_M_next').GetValueAsUnsigned(0) != self.get_end_of_list_address(): | ||||
size = size + 1 | size = size + 1 | ||||
current = current.GetChildMemberWithName('_M_next') | current = current.GetChildMemberWithName('_M_next') | ||||
return size | return size | ||||
except: | except: | ||||
logger >> "Error determining the size" | logger >> "Error determining the size" | ||||
return 0 | return 0 | ||||
def get_child_index(self, name): | def get_child_index(self, name): | ||||
logger = lldb.formatters.Logger.Logger() | logger = lldb.formatters.Logger.Logger() | ||||
try: | try: | ||||
return int(name.lstrip('[').rstrip(']')) | return int(name.lstrip('[').rstrip(']')) | ||||
except: | except: | ||||
return -1 | return -1 | ||||
def get_child_at_index(self, index): | def get_child_at_index(self, index): | ||||
logger = lldb.formatters.Logger.Logger() | logger = lldb.formatters.Logger.Logger() | ||||
logger >> "Fetching child " + str(index) | logger >> "Fetching child " + str(index) | ||||
if index < 0: | if index < 0: | ||||
return None | return None | ||||
if index >= self.num_children(): | if index >= self.num_children(): | ||||
return None | return None | ||||
try: | try: | ||||
Show All 40 Lines | class AbstractListSynthProvider: | ||||
Method is used to extract the list pointers into the variables (e.g self.node, self.next, and optionally to self.prev) | Method is used to extract the list pointers into the variables (e.g self.node, self.next, and optionally to self.prev) | ||||
and is mandatory to be overriden in each AbstractListSynthProvider subclass | and is mandatory to be overriden in each AbstractListSynthProvider subclass | ||||
''' | ''' | ||||
def updateNodes(self): | def updateNodes(self): | ||||
raise NotImplementedError | raise NotImplementedError | ||||
def has_children(self): | def has_children(self): | ||||
return True | return True | ||||
''' | ''' | ||||
Method is used to identify if a node traversal has reached its end | Method is used to identify if a node traversal has reached its end | ||||
and is mandatory to be overriden in each AbstractListSynthProvider subclass | and is mandatory to be overriden in each AbstractListSynthProvider subclass | ||||
''' | ''' | ||||
def get_end_of_list_address(self): | def get_end_of_list_address(self): | ||||
raise NotImplementedError | raise NotImplementedError | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | class StdVectorImplementation(object): | ||||
# point in trying to fetch anything | # point in trying to fetch anything | ||||
if self.start.IsValid() and self.finish.IsValid( | if self.start.IsValid() and self.finish.IsValid( | ||||
) and self.end.IsValid() and self.data_type.IsValid(): | ) and self.end.IsValid() and self.data_type.IsValid(): | ||||
self.count = None | self.count = None | ||||
else: | else: | ||||
self.count = 0 | self.count = 0 | ||||
except: | except: | ||||
pass | pass | ||||
return False | return False | ||||
class StdVBoolImplementation(object): | class StdVBoolImplementation(object): | ||||
def __init__(self, valobj, bool_type): | def __init__(self, valobj, bool_type): | ||||
self.valobj = valobj | self.valobj = valobj | ||||
self.bool_type = bool_type | self.bool_type = bool_type | ||||
self.valid = False | self.valid = False | ||||
▲ Show 20 Lines • Show All 247 Lines • Show Last 20 Lines |
I assume this is relying on the fact that _M_engaged can only contain values 1 and 0. While this may be true for normal situations, people tend to use debuggers precisely in the situations where things are not "normal". And it'd be good if e.g. a user's memory corruption bug (which for example overwrites this field) does not make the debugger go haywire. You most likely cannot format the value correctly if that happens, but you can at least avoid printing 3195786238 children in those cases.
One way to do that would be to rename this field to engaged and implement the num_children method as return self.engaged ? 1 : 0