memgraph/tools/gdb-plugins/operator_tree.py
Teon Banek f069677b39 Add print-operator-tree command to gdb
Summary:
This is a simple extension to gdb, which will print the LogicalOperator
tree. Auto loading of the extension is supported via .gdbinit if the gdb
is run from the root of the repository.

Explicit loading of scripts can be done inside gdb by running
`source path/to/extension.py`.

Reviewers: florijan, mislav.bradac, buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D428
2017-06-08 09:37:05 +02:00

61 lines
2.2 KiB
Python

import io
import gdb
def _logical_operator_type():
'''Returns the LogicalOperator gdb.Type'''
# This is a function, because the type may appear during gdb runtime.
# Therefore, we cannot assign it on import.
return gdb.lookup_type('query::plan::LogicalOperator')
def _shared_ptr_pointee(shared_ptr):
'''Returns the address of the pointed to object inside shared_ptr.'''
# This function may not be needed when gdb adds dereferencing shared_ptr
# via Python API.
with io.StringIO() as string_io:
print(shared_ptr, file=string_io)
addr = string_io.getvalue().split()[-1]
return int(addr, base=16)
def _get_operator_input(operator):
'''Returns the input operator of given operator, if it has any.'''
if 'input_' not in [f.name for f in operator.type.fields()]:
return None
input_addr = _shared_ptr_pointee(operator['input_'])
if input_addr == 0:
return None
pointer_type = _logical_operator_type().pointer()
input_op = gdb.Value(input_addr).cast(pointer_type).dereference()
return input_op.cast(input_op.dynamic_type)
class PrintOperatorTree(gdb.Command):
'''Print the tree of logical operators from the expression.'''
def __init__(self):
super(PrintOperatorTree, self).__init__("print-operator-tree",
gdb.COMMAND_USER)
def invoke(self, argument, from_tty):
try:
operator = gdb.parse_and_eval(argument)
except gdb.error as e:
raise gdb.GdbError(*e.args)
# Currently, gdb doesn't provide API to check if the dynamic_type is
# subtype of a base type. So, this check will fail, for example if we
# get 'query::plan::ScanAll'. The user can avoid this by up-casting.
if operator.type != _logical_operator_type():
raise gdb.GdbError("Expected a '%s', but got '%s'" %
(_logical_operator_type(), operator.type))
next_op = operator.cast(operator.dynamic_type)
tree = []
while next_op is not None:
tree.append('* %s' % next_op.type.name)
next_op = _get_operator_input(next_op)
print('\n'.join(tree))
PrintOperatorTree()