Browse Source

feat(dispatch): add gdb debug utils

GitOrigin-RevId: 393b0c9c76
tags/v1.8.0
Megvii Engine Team 3 years ago
parent
commit
c3d63f14a5
3 changed files with 246 additions and 3 deletions
  1. +1
    -0
      .gdbinit
  2. +210
    -0
      scripts/gdb/commands.py
  3. +35
    -3
      scripts/gdb/pretty_printers.py

+ 1
- 0
.gdbinit View File

@@ -1,3 +1,4 @@
source ./scripts/gdb/commands.py
source ./scripts/gdb/pretty_printers.py
source ./scripts/gdb/xmethods.py
catch throw

+ 210
- 0
scripts/gdb/commands.py View File

@@ -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<mgb::imperative::ValueRef>\)$")
mge_op_fallback_pattern = re.compile(r"^(.*)::fallback\(mgb::imperative::Span<mgb::imperative::ValueRef>\) 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<mgb::imperative::ValueRef>)")


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*)")

+ 35
- 3
scripts/gdb/pretty_printers.py View File

@@ -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)




Loading…
Cancel
Save