f069677b39
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
61 lines
2.2 KiB
Python
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()
|