From c3d63f14a5fb6c87bbd312893ebdd242545c695c Mon Sep 17 00:00:00 2001 From: Megvii Engine Team Date: Fri, 14 Jan 2022 13:13:03 +0800 Subject: [PATCH] feat(dispatch): add gdb debug utils GitOrigin-RevId: 393b0c9c76c29aacfcf917fd6e5479f5a9917a6b --- .gdbinit | 1 + scripts/gdb/commands.py | 210 +++++++++++++++++++++++++++++++++ scripts/gdb/pretty_printers.py | 38 +++++- 3 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 scripts/gdb/commands.py diff --git a/.gdbinit b/.gdbinit index 80cad4295..867d9524a 100644 --- a/.gdbinit +++ b/.gdbinit @@ -1,3 +1,4 @@ +source ./scripts/gdb/commands.py source ./scripts/gdb/pretty_printers.py source ./scripts/gdb/xmethods.py catch throw diff --git a/scripts/gdb/commands.py b/scripts/gdb/commands.py new file mode 100644 index 000000000..998c15ac6 --- /dev/null +++ b/scripts/gdb/commands.py @@ -0,0 +1,210 @@ +import gdb + +import re +import subprocess + + +_demangle_cache = {} + + +def demangle(name): + if name not in _demangle_cache: + _demangle_cache[name] = subprocess.run(['c++filt', "-t", name], stdout=subprocess.PIPE).stdout + return _demangle_cache[name].decode('utf-8').strip() + + +def dynamic_cast(val): + assert val.type.code == gdb.TYPE_CODE_REF + val = val.cast(val.dynamic_type) + return val + + +def eval_on_val(val, eval_str): + if val.type.code == gdb.TYPE_CODE_REF: + val = val.referenced_value() + address = val.address + eval_str = "(*({}){}){}".format(address.type, address, eval_str) + return gdb.parse_and_eval(eval_str) + + +def is_subclass_of(subclass, baseclass): + for field in subclass.fields(): + if field.is_base_class: + if field.type == baseclass: + return True + elif is_subclass_of(field.type, baseclass): + return True + + +def vector_size(vector): + impl = vector["_M_impl"] + return int(impl["_M_finish"] - impl["_M_start"]) + + +def vector_item(vector, i): + impl = vector["_M_impl"] + return (impl["_M_start"] + i).dereference() + + +def shared_ptr_deref(ptr): + return ptr["_M_ptr"].dereference() + + +def get_type_name(type_index): + return gdb.lookup_global_symbol("mgb::imperative::debug::get_type_name(std::type_index const&)").value()(type_index).string() + + +mge_apply_transform_pattern = re.compile(r"^(.*)::apply_transform\(mgb::imperative::Operator const&, mgb::imperative::Span\)$") +mge_op_fallback_pattern = re.compile(r"^(.*)::fallback\(mgb::imperative::Span\) const$") + + +def is_mge_frame(frame): + function = frame.function() + if not (function and function.name): + return False + matcher = mge_apply_transform_pattern.match(function.name) + if matcher: + typename = matcher.group(1) + return is_subclass_of(gdb.lookup_type(typename), gdb.lookup_type("mgb::imperative::Transform")) + matcher = mge_op_fallback_pattern.match(function.name) + if matcher: + typename = matcher.group(1) + return is_subclass_of(gdb.lookup_type(typename), gdb.lookup_type("mgb::imperative::Operator")) + return False + + +def count_frame_level(frame): + level = -1 + while frame: + frame = frame.newer() + level += 1 + return level + + +def print_mge_frame(frame): + function = frame.function() + op = eval_on_val(dynamic_cast(frame.read_var("op")), ".to_string().c_str()").string() + inputs = str(frame.read_var("inputs")) + matcher = mge_apply_transform_pattern.match(function.name) + if matcher: + name = matcher.group(1) + else: + name = mge_op_fallback_pattern.match(function.name).group(1) + #TODO: span + sal = frame.find_sal() + filename = sal.symtab.filename + line = sal.line + print("#{} {} apply {} on {}, file: {}:{}".format(count_frame_level(frame), name, op, inputs, filename, line)) + + +class MegengineBacktrace(gdb.Command): + def __init__(self): + super().__init__("mge-bt", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + frame = gdb.newest_frame() + mge_stack = [] + while frame is not None: + if is_mge_frame(frame): + mge_stack.append(frame) + frame = frame.older() + for frame in reversed(mge_stack): + print_mge_frame(frame) + + +class MegengineBreakApply(gdb.Command): + def __init__(self): + super().__init__("mge-brk-apply", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + gdb.Breakpoint("mgb::imperative::apply(mgb::imperative::Operator const&, mgb::imperative::Span)") + + +class MegengineWatch(gdb.Command): + def __init__(self): + super().__init__("mge-watch", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + watch = gdb.lookup_global_symbol("mgb::imperative::debug::watch_value(mgb::imperative::ValueRef)").value() + value = gdb.parse_and_eval(arg) + watch(value) + print("watching {}".format(str(value))) + + +class MegengineUp(gdb.Command): + def __init__(self): + super().__init__("mge-up", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + frame = gdb.selected_frame() + if frame is None: + print("Unable to find older dispatch frame") + return + frame = frame.older() + while frame is not None and not is_mge_frame(frame): + frame = frame.older() + if frame is None: + print("Unable to find older dispatch frame") + return + frame.select() + print_mge_frame(frame) + + +class MegengineDown(gdb.Command): + def __init__(self): + super().__init__("mge-down", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + frame = gdb.selected_frame() + if frame is None: + print("Unable to find newer dispatch frame") + return + frame = frame.newer() + while frame is not None and not is_mge_frame(frame): + frame = frame.newer() + if frame is None: + print("Unable to find newer dispatch frame") + return + frame.select() + print_mge_frame(frame) + + +class MegengineInfo(gdb.Command): + def __init__(self): + super().__init__("mge-info", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + if arg == "opr": + registered_oprs = gdb.lookup_global_symbol("mgb::imperative::Operator::registered_types()").value()() + size = vector_size(registered_oprs) + for i in range(size): + registered_opr = vector_item(registered_oprs, i) + # name = eval_on_val(registered_opr, ".name()").string() + name = get_type_name(registered_opr) + print("{}: {}".format(i, demangle(name))) + elif arg == "trf": + dispatch_context = gdb.lookup_global_symbol("mgb::imperative::Transform::get_context()").value()() + transformations = dispatch_context["transformations"] + size = vector_size(transformations) + for i in range(size): + transformation = vector_item(transformations, i) + # name = eval_on_val(transformation, ".get()->name().c_str()").string() + name = shared_ptr_deref(transformation).dynamic_type.name + # name = get_type_name(transformation) + print("{}: {}".format(i, demangle(name))) + else: + print("Unsupported argument {}".format(arg)) + + def complete(self, text, word): + return ["opr", "trf"] + + + +MegengineBacktrace() +MegengineBreakApply() +MegengineUp() +MegengineDown() +MegengineInfo() +MegengineWatch() + +gdb.Breakpoint("mgb::imperative::debug::notify_event(char const*)") \ No newline at end of file diff --git a/scripts/gdb/pretty_printers.py b/scripts/gdb/pretty_printers.py index adb0f9819..d7dd65b63 100644 --- a/scripts/gdb/pretty_printers.py +++ b/scripts/gdb/pretty_printers.py @@ -3,8 +3,17 @@ import gdb.printing import gdb.types +def dynamic_cast(val): + assert val.type.code == gdb.TYPE_CODE_REF + val = val.cast(val.dynamic_type) + return val + + def eval_on_val(val, eval_str): - eval_str = "(*({}*)({})).{}".format(val.type, val.address, eval_str) + if val.type.code == gdb.TYPE_CODE_REF: + val = val.referenced_value() + address = val.address + eval_str = "(*({}){}){}".format(address.type, int(address), eval_str) return gdb.parse_and_eval(eval_str) @@ -50,7 +59,7 @@ class ToStringPrinter: self.val = val def to_string(self): - return eval_on_val(self.val, "to_string().c_str()").string() + return eval_on_val(self.val, ".to_string().c_str()").string() class ReprPrinter: @@ -58,7 +67,10 @@ class ReprPrinter: self.val = val def to_string(self): - return eval_on_val(self.val, "repr().c_str()").string() + val = self.val + if val.type.code == gdb.TYPE_CODE_REF: + val = val.referenced_value() + return eval_on_val(val, ".repr().c_str()").string() class HandlePrinter: @@ -141,6 +153,23 @@ class OpDefPrinter: yield field.name, concrete_val[field.name] +class SpanPrinter: + def __init__(self, val): + self.begin = val['m_begin'] + self.end = val['m_end'] + self.size = self.end - self.begin + + def to_string(self): + return 'Span of Size {}'.format(self.size) + + def display_hint(self): + return 'array' + + def children(self): + for i in range(self.size): + yield "[{}]".format(i), (self.begin+i).dereference() + + pp = gdb.printing.RegexpCollectionPrettyPrinter("MegEngine") # megdnn pp.add_printer('megdnn::SmallVectorImpl', '^megdnn::SmallVector(Impl)?<.*>$', SmallVectorPrinter) @@ -154,6 +183,9 @@ pp.add_printer('mgb::imperative::LogicalTensorDesc', '^mgb::imperative::LogicalT pp.add_printer('mgb::imperative::OpDef', '^mgb::imperative::OpDef$', OpDefPrinter) pp.add_printer('mgb::imperative::Subgraph', '^mgb::imperative::Subgraph$', ReprPrinter) pp.add_printer('mgb::imperative::EncodedSubgraph', '^mgb::imperative::EncodedSubgraph$', ReprPrinter) +# imperative dispatch +pp.add_printer('mgb::imperative::ValueRef', '^mgb::imperative::ValueRef$', ToStringPrinter) +pp.add_printer('mgb::imperative::Span', '^mgb::imperative::Span<.*>$', SpanPrinter) gdb.printing.register_pretty_printer(gdb.current_objfile(), pp) -- GitLab