Newer
Older
Johnny Chen
committed
#
Johnny Chen
committed
#
# This script modifies the lldb module (which was automatically generated via
# running swig) to support iteration and/or equality operations for certain lldb
# objects, implements truth value testing for certain lldb objects, and adds a
# global variable 'debugger_unique_id' which is initialized to 0.
Johnny Chen
committed
#
# As a cleanup step, it also removes the 'residues' from the autodoc features of
# swig. For an example, take a look at SBTarget.h header file, where we take
Johnny Chen
committed
# advantage of the already existing doxygen C++-docblock and make it the Python
# docstring for the same method. The 'residues' in this context include the
Johnny Chen
committed
# '#endif', the '#ifdef SWIG', the c comment marker, the trailing blank (SPC's)
# line, and the doxygen comment start marker.
#
Johnny Chen
committed
# In addition to the 'residues' removal during the cleanup step, it also
# transforms the 'char' data type (which was actually 'char *' but the 'autodoc'
# feature of swig removes ' *' from it into 'str' (as a Python str type).
#
Johnny Chen
committed
# It also calls SBDebugger.Initialize() to initialize the lldb debugger
# subsystem.
#
import sys, re, StringIO
if len (sys.argv) != 2:
output_name = "./lldb.py"
else:
output_name = sys.argv[1] + "/lldb.py"
# print "output_name is '" + output_name + "'"
Johnny Chen
committed
#
# Residues to be removed.
Johnny Chen
committed
#
c_endif_swig = "#endif"
c_ifdef_swig = "#ifdef SWIG"
Johnny Chen
committed
c_comment_marker = "//------------"
# The pattern for recognizing the doxygen comment block line.
Johnny Chen
committed
doxygen_comment_start = re.compile("^\s*(/// ?)")
Johnny Chen
committed
# The demarcation point for turning on/off residue removal state.
# When bracketed by the lines, the CLEANUP_DOCSTRING state (see below) is ON.
toggle_docstring_cleanup_line = ' """'
Johnny Chen
committed
def char_to_str_xform(line):
"""This transforms the 'char', i.e, 'char *' to 'str', Python string."""
line = line.replace(' char', ' str')
line = line.replace('char ', 'str ')
# Special case handling of 'char **argv' and 'char **envp'.
line = line.replace('str argv', 'list argv')
line = line.replace('str envp', 'list envp')
Johnny Chen
committed
return line
#
# The one-liner docstring also needs char_to_str transformation, btw.
#
Johnny Chen
committed
TWO_SPACES = ' ' * 2
EIGHT_SPACES = ' ' * 8
one_liner_docstring_pattern = re.compile('^(%s|%s)""".*"""$' % (TWO_SPACES, EIGHT_SPACES))
Johnny Chen
committed
Johnny Chen
committed
#
# lldb_iter() should appear before our first SB* class definition.
Johnny Chen
committed
#
lldb_iter_def = '''
# ===================================
# Iterator for lldb container objects
# ===================================
def lldb_iter(obj, getsize, getelem):
"""A generator adaptor to support iteration for lldb container objects."""
size = getattr(obj, getsize)
elem = getattr(obj, getelem)
for i in range(size()):
yield elem(i)
Johnny Chen
committed
# ==============================================================================
# The modify-python-lldb.py script is responsible for post-processing this SWIG-
# generated lldb.py module. It is responsible for adding the above lldb_iter()
# function definition as well as the supports, in the following, for iteration
# protocol: __iter__, rich comparison methods: __eq__ and __ne__, truth value
# testing (and built-in operation bool()): __nonzero__, and built-in function
# len(): __len__.
# ==============================================================================
Johnny Chen
committed
'''
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#
# linked_list_iter() is a special purpose iterator to treat the SBValue as a
# list data structure, where you specify the child member name which points to
# the next item on the list and you specify the end-of-list function which takes
# an SBValue and returns True if EOL is reached and False if not.
#
linked_list_iter_def = '''
# ==================================================
# Iterator for lldb.SBValue treated as a linked list
# ==================================================
def linked_list_iter(self, next_item_name, end_of_list):
"""A generator adaptor to support iteration for SBValue as a linked list.
For example,
# Test function to determine end of list.
def eol(val):
if not val:
return True
try:
# Test the semantics of the item we got.
id = val.GetChildMemberWithName("id")
if int(id.GetValue()) > 0:
return False
except:
pass
# If we fall through to here. It could be that exception
# occurred or the "id" child member does not qualify as a
# valid item. Return True for EOL.
return True
# Get Frame #0.
...
# Get variable 'task_head'.
task_head = frame0.FindVariable('task_head')
...
for t in task_head.linked_list_iter('next', eol):
print t
"""
try:
item = self.GetChildMemberWithName(next_item_name)
while item:
yield item
# Prepare for the next iteration.
item = item.GetChildMemberWithName(next_item_name)
if end_of_list(item):
break
except:
# Exception occurred. Stop the generator.
pass
return
'''
Johnny Chen
committed
# This supports the iteration protocol.
iter_def = " def __iter__(self): return lldb_iter(self, '%s', '%s')"
module_iter = " def module_iter(self): return lldb_iter(self, '%s', '%s')"
breakpoint_iter = " def breakpoint_iter(self): return lldb_iter(self, '%s', '%s')"
Johnny Chen
committed
# Called to implement the built-in function len().
# Eligible objects are those containers with unambiguous iteration support.
len_def = " def __len__(self): return self.%s()"
Johnny Chen
committed
Johnny Chen
committed
# This supports the rich comparison methods of __eq__ and __ne__.
eq_def = " def __eq__(self, other): return isinstance(other, %s) and %s"
Johnny Chen
committed
ne_def = " def __ne__(self, other): return not self.__eq__(other)"
Johnny Chen
committed
Johnny Chen
committed
# Called to implement truth value testing and the built-in operation bool();
# should return False or True, or their integer equivalents 0 or 1.
# Delegate to self.IsValid() if it is defined for the current lldb object.
nonzero_def = " def __nonzero__(self): return self.IsValid()"
Johnny Chen
committed
#
# This dictionary defines a mapping from classname to (getsize, getelem) tuple.
Johnny Chen
committed
#
d = { 'SBBreakpoint': ('GetNumLocations', 'GetLocationAtIndex'),
'SBCompileUnit': ('GetNumLineEntries', 'GetLineEntryAtIndex'),
'SBDebugger': ('GetNumTargets', 'GetTargetAtIndex'),
'SBModule': ('GetNumSymbols', 'GetSymbolAtIndex'),
'SBProcess': ('GetNumThreads', 'GetThreadAtIndex'),
'SBThread': ('GetNumFrames', 'GetFrameAtIndex'),
'SBInstructionList': ('GetSize', 'GetInstructionAtIndex'),
'SBStringList': ('GetSize', 'GetStringAtIndex',),
'SBSymbolContextList': ('GetSize', 'GetContextAtIndex'),
'SBValueList': ('GetSize', 'GetValueAtIndex'),
'SBType': ('GetNumberChildren', 'GetChildAtIndex'),
'SBValue': ('GetNumChildren', 'GetChildAtIndex'),
Johnny Chen
committed
'SBTarget': {'module': ('GetNumModules', 'GetModuleAtIndex'),
'breakpoint': ('GetNumBreakpoints', 'GetBreakpointAtIndex')
}
}
Johnny Chen
committed
#
# This dictionary defines a mapping from classname to equality method name(s).
Johnny Chen
committed
#
e = { 'SBAddress': ['GetFileAddress', 'GetModule'],
'SBBreakpoint': ['GetID'],
'SBFileSpec': ['GetFilename', 'GetDirectory'],
'SBModule': ['GetFileSpec', 'GetUUIDString']
}
def list_to_frag(list):
"""Transform a list to equality program fragment.
For example, ['GetID'] is transformed to 'self.GetID() == other.GetID()',
and ['GetFilename', 'GetDirectory'] to 'self.GetFilename() == other.GetFilename()
and self.GetDirectory() == other.GetDirectory()'.
"""
if not list:
raise Exception("list should be non-empty")
frag = StringIO.StringIO()
for i in range(len(list)):
if i > 0:
frag.write(" and ")
frag.write("self.{0}() == other.{0}()".format(list[i]))
return frag.getvalue()
Johnny Chen
committed
Johnny Chen
committed
class NewContent(StringIO.StringIO):
"""Simple facade to keep track of the previous line to be committed."""
def __init__(self):
StringIO.StringIO.__init__(self)
self.prev_line = None
def add_line(self, a_line):
"""Add a line to the content, if there is a previous line, commit it."""
if self.prev_line != None:
print >> self, self.prev_line
self.prev_line = a_line
def del_line(self):
"""Forget about the previous line, do not commit it."""
self.prev_line = None
def del_blank_line(self):
"""Forget about the previous line if it is a blank line."""
if self.prev_line != None and not self.prev_line.strip():
self.prev_line = None
def finish(self):
"""Call this when you're finished with populating content."""
if self.prev_line != None:
print >> self, self.prev_line
self.prev_line = None
Johnny Chen
committed
# The new content will have the iteration protocol defined for our lldb objects.
Johnny Chen
committed
new_content = NewContent()
Johnny Chen
committed
with open(output_name, 'r') as f_in:
content = f_in.read()
# The pattern for recognizing the beginning of an SB class definition.
class_pattern = re.compile("^class (SB.*)\(_object\):$")
# The pattern for recognizing the beginning of the __init__ method definition.
init_pattern = re.compile("^ def __init__\(self, \*args\):")
Johnny Chen
committed
# The pattern for recognizing the beginning of the IsValid method definition.
isvalid_pattern = re.compile("^ def IsValid\(")
Johnny Chen
committed
# These define the states of our finite state machine.
Johnny Chen
committed
NORMAL = 0
DEFINING_ITERATOR = 1
Johnny Chen
committed
DEFINING_EQUALITY = 2
Johnny Chen
committed
CLEANUP_DOCSTRING = 4
Johnny Chen
committed
# The lldb_iter_def only needs to be inserted once.
lldb_iter_defined = False;
Johnny Chen
committed
# Our FSM begins its life in the NORMAL state, and transitions to the
# DEFINING_ITERATOR and/or DEFINING_EQUALITY state whenever it encounters the
# beginning of certain class definitions, see dictionaries 'd' and 'e' above.
#
# Note that the two states DEFINING_ITERATOR and DEFINING_EQUALITY are
# orthogonal in that our FSM can be in one, the other, or both states at the
# same time. During such time, the FSM is eagerly searching for the __init__
# method definition in order to insert the appropriate method(s) into the lldb
# module.
#
Johnny Chen
committed
# The state CLEANUP_DOCSTRING can be entered from either the NORMAL or the
# DEFINING_ITERATOR/EQUALITY states. While in this state, the FSM is fixing/
# cleaning the Python docstrings generated by the swig docstring features.
#
Johnny Chen
committed
# The FSM, in all possible states, also checks the current input for IsValid()
# definition, and inserts a __nonzero__() method definition to implement truth
# value testing and the built-in operation bool().
Johnny Chen
committed
state = NORMAL
for line in content.splitlines():
Johnny Chen
committed
# Handle the state transition into CLEANUP_DOCSTRING state as it is possible
# to enter this state from either NORMAL or DEFINING_ITERATOR/EQUALITY.
#
# If ' """' is the sole line, prepare to transition to the
# CLEANUP_DOCSTRING state or out of it.
if line == toggle_docstring_cleanup_line:
if state & CLEANUP_DOCSTRING:
Johnny Chen
committed
# Special handling of the trailing blank line right before the '"""'
# end docstring marker.
new_content.del_blank_line()
Johnny Chen
committed
state ^= CLEANUP_DOCSTRING
else:
state |= CLEANUP_DOCSTRING
Johnny Chen
committed
if state == NORMAL:
match = class_pattern.search(line)
Johnny Chen
committed
# Inserts the lldb_iter() definition before the first class definition.
Johnny Chen
committed
if not lldb_iter_defined and match:
Johnny Chen
committed
new_content.add_line(lldb_iter_def)
Johnny Chen
committed
lldb_iter_defined = True
Johnny Chen
committed
# If we are at the beginning of the class definitions, prepare to
# transition to the DEFINING_ITERATOR/DEFINING_EQUALITY state for the
# right class names.
Johnny Chen
committed
if match:
Johnny Chen
committed
cls = match.group(1)
Johnny Chen
committed
if cls in d:
# Adding support for iteration for the matched SB class.
Johnny Chen
committed
state |= DEFINING_ITERATOR
Johnny Chen
committed
if cls in e:
# Adding support for eq and ne for the matched SB class.
Johnny Chen
committed
state |= DEFINING_EQUALITY
Johnny Chen
committed
if (state & DEFINING_ITERATOR) or (state & DEFINING_EQUALITY):
Johnny Chen
committed
match = init_pattern.search(line)
if match:
# We found the beginning of the __init__ method definition.
Johnny Chen
committed
# This is a good spot to insert the iter and/or eq-ne support.
Johnny Chen
committed
#
# But note that SBTarget has two types of iterations.
if cls == "SBTarget":
Johnny Chen
committed
new_content.add_line(module_iter % (d[cls]['module']))
new_content.add_line(breakpoint_iter % (d[cls]['breakpoint']))
Johnny Chen
committed
else:
Johnny Chen
committed
if (state & DEFINING_ITERATOR):
Johnny Chen
committed
new_content.add_line(iter_def % d[cls])
new_content.add_line(len_def % d[cls][0])
Johnny Chen
committed
if (state & DEFINING_EQUALITY):
Johnny Chen
committed
new_content.add_line(eq_def % (cls, list_to_frag(e[cls])))
new_content.add_line(ne_def)
# This special purpose iterator is for SBValue only!!!
if cls == "SBValue":
new_content.add_line(linked_list_iter_def)
# Next state will be NORMAL.
state = NORMAL
Johnny Chen
committed
Johnny Chen
committed
if (state & CLEANUP_DOCSTRING):
Johnny Chen
committed
# Cleanse the lldb.py of the autodoc'ed residues.
if c_ifdef_swig in line or c_endif_swig in line:
continue
Johnny Chen
committed
# As well as the comment marker line.
if c_comment_marker in line:
Johnny Chen
committed
continue
Johnny Chen
committed
Johnny Chen
committed
# Also remove the '\a ' and '\b 'substrings.
Johnny Chen
committed
line = line.replace('\a ', '')
Johnny Chen
committed
line = line.replace('\b ', '')
Johnny Chen
committed
# And the leading '///' substring.
doxygen_comment_match = doxygen_comment_start.match(line)
if doxygen_comment_match:
line = line.replace(doxygen_comment_match.group(1), '', 1)
Johnny Chen
committed
line = char_to_str_xform(line)
Johnny Chen
committed
# Note that the transition out of CLEANUP_DOCSTRING is handled at the
# beginning of this function already.
Johnny Chen
committed
# This deals with one-liner docstring, for example, SBThread.GetName:
# """GetName(self) -> char""".
if one_liner_docstring_pattern.match(line):
line = char_to_str_xform(line)
Johnny Chen
committed
# Look for 'def IsValid(*args):', and once located, add implementation
# of truth value testing for this object by delegation.
if isvalid_pattern.search(line):
Johnny Chen
committed
new_content.add_line(nonzero_def)
Johnny Chen
committed
# Pass the original line of content to new_content.
Johnny Chen
committed
new_content.add_line(line)
# We are finished with recording new content.
new_content.finish()
Johnny Chen
committed
with open(output_name, 'w') as f_out:
f_out.write(new_content.getvalue())
f_out.write("debugger_unique_id = 0\n")
f_out.write("SBDebugger.Initialize()\n")