2017-06-08 15:05:24 +08:00
|
|
|
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:
|
2017-07-07 16:30:44 +08:00
|
|
|
tree.append('* %s <%s>' % (next_op.type.name, next_op.address))
|
2017-06-08 15:05:24 +08:00
|
|
|
next_op = _get_operator_input(next_op)
|
|
|
|
print('\n'.join(tree))
|
|
|
|
|
|
|
|
|
|
|
|
PrintOperatorTree()
|