Getting Started¶
equip has a simple interface that contains a handful of important classes to work with:
- Instrument
- SimpleRewriter
- MethodVisitor
Instrument¶
Main interface for the instrumentation. It triggers the conversion from the bytecode to the internal representation, as well as executing the visitors and writing back the resulting bytecode.
The workflow of Instrument
requires the following steps:
Pass the location (or locations) to the Instrument:
instr.location = ['path/to/module', 'path/to/other/module']Ask
Instrument
to prepare the program by compiling the sources (if necessary or requested) and creating a list of bytecode files that can be instrumented:if not instr.prepare_program(): raise Exception('Error while compiling the code...')Apply the visitor on all bytecode files and persist the new bytecode:
instr.apply(my_visitor, rewrite=True)
The compilation of the program is not performed by default as the program might already be compiled, and the bytecode ready to consume. If however, we want to force rebuilding the bytecode for the entire application, we can set the force-rebuild option between step 1 and 2:
instr.set_option('force-rebuild')
Visitors¶
The Instrument
creates a representation for each pyc file that contains different
Declaration
objects. A visitor can be created to iterate over these Declaration
.
The most commonly used visitor is the MethodVisitor
that is triggered over all method
declarations found in the bytecode.
Here’s an example of a visitor that prints the start and end line for each method:
class MethodLinesVisitor(MethodVisitor):
def __init__(self):
MethodVisitor.__init__(self)
def visit(self, meth_decl):
print "Method %s: start=%d, end=%d" \
% (meth_decl.method_name, meth_decl.start_lineno, meth_decl.end_lineno)
SimpleRewriter¶
Handles the insertion of bytecode, and generation of proper bytecode. The rewriter allows for multiple operations such as:
- Insert generic bytecode
- Insert import statements
- Insert on_enter/on_exit callbacks
The rewriter is called from within a visitor or any other way to get a particular Declaration
.
It consumes the Declaration
and allows for inserting bytecode at any desired point in the
original bytecode.
For example, we can add create an instrumentation to insert for all returns in a method:
ON_AFTER = """
print "Exit {method_name}, return value := %s" % repr({return_value})
"""
class ReturnValuesVisitor(MethodVisitor):
def __init__(self):
MethodVisitor.__init__(self)
def visit(self, meth_decl):
rewriter = SimpleRewriter(meth_decl)
rewriter.insert_after(ON_AFTER)
Note that the Instrument
is currently responsible for applying the changes, which means
serializing the declarations of the current bytecode.