Signed-off-by: Benign X <1341398182@qq.com> Co-authored-by: Neo Xu <neo.xu1990@gmail.com>
336 lines
9.1 KiB
Python
336 lines
9.1 KiB
Python
import argparse
|
|
from typing import Iterator, Union, Optional
|
|
|
|
import gdb
|
|
|
|
from .value import Value
|
|
|
|
gdb.execute("set pagination off")
|
|
gdb.write("set pagination off\n")
|
|
gdb.execute("set python print-stack full")
|
|
gdb.write("set python print-stack full\n")
|
|
|
|
g_lvgl_instance = None
|
|
|
|
|
|
class LVList(Value):
|
|
"""LVGL linked list iterator"""
|
|
|
|
def __init__(self, ll: Value, nodetype: Union[gdb.Type, str] = None):
|
|
if not ll:
|
|
raise ValueError("Invalid linked list")
|
|
super().__init__(ll)
|
|
|
|
self.nodetype = (
|
|
gdb.lookup_type(nodetype).pointer()
|
|
if isinstance(nodetype, str)
|
|
else nodetype
|
|
)
|
|
self.lv_ll_node_t = gdb.lookup_type("lv_ll_node_t").pointer()
|
|
self.current = self.head
|
|
self._next_offset = self.n_size + self.lv_ll_node_t.sizeof
|
|
self._prev_offset = self.n_size
|
|
|
|
def _next(self, node):
|
|
next_value = Value(int(node) + self._next_offset)
|
|
return next_value.cast(self.lv_ll_node_t, ptr=True).dereference()
|
|
|
|
def _prev(self, node):
|
|
prev_value = Value(int(node) + self._prev_offset)
|
|
return prev_value.cast(self.lv_ll_node_t, ptr=True).dereference()
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def __next__(self):
|
|
if not self.current:
|
|
raise StopIteration
|
|
|
|
nodetype = self.nodetype if self.nodetype else self.lv_ll_node_t
|
|
node = self.current.cast(nodetype)
|
|
|
|
self.current = self._next(self.current)
|
|
return node
|
|
|
|
@property
|
|
def len(self):
|
|
len = 0
|
|
node = self.head
|
|
while node:
|
|
len += 1
|
|
node = self._next(node)
|
|
return len
|
|
|
|
|
|
class LVObject(Value):
|
|
"""LVGL object"""
|
|
|
|
def __init__(self, obj: Value):
|
|
super().__init__(obj.cast("lv_obj_t", ptr=True))
|
|
|
|
@property
|
|
def class_name(self):
|
|
name = self.class_p.name
|
|
return name.string() if name else "unknown"
|
|
|
|
@property
|
|
def x1(self):
|
|
return int(self.coords.x1)
|
|
|
|
@property
|
|
def y1(self):
|
|
return int(self.coords.y1)
|
|
|
|
@property
|
|
def x2(self):
|
|
return int(self.coords.x2)
|
|
|
|
@property
|
|
def y2(self):
|
|
return int(self.coords.y2)
|
|
|
|
@property
|
|
def child_count(self):
|
|
return self.spec_attr.child_cnt if self.spec_attr else 0
|
|
|
|
@property
|
|
def childs(self):
|
|
if not self.spec_attr:
|
|
return
|
|
|
|
for i in range(self.child_count):
|
|
child = self.spec_attr.children[i]
|
|
yield LVObject(child)
|
|
|
|
@property
|
|
def styles(self):
|
|
LV_STYLE_PROP_INV = 0
|
|
LV_STYLE_PROP_ANY = 0xFF
|
|
count = self.style_cnt
|
|
if count == 0:
|
|
return
|
|
|
|
styles = self.super_value("styles")
|
|
for i in range(count):
|
|
style = styles[i].style
|
|
prop_cnt = style.prop_cnt
|
|
values_and_props = style.values_and_props.cast("lv_style_const_prop_t", ptr=True)
|
|
for j in range(prop_cnt):
|
|
prop = values_and_props[j].prop
|
|
if prop == LV_STYLE_PROP_INV or prop == LV_STYLE_PROP_ANY:
|
|
continue
|
|
yield values_and_props[j]
|
|
|
|
def get_child(self, index: int):
|
|
return (
|
|
self.spec_attr.children[index] if self.spec_attr else None
|
|
)
|
|
|
|
|
|
class LVDisplay(Value):
|
|
"""LVGL display"""
|
|
|
|
def __init__(self, disp: Value):
|
|
super().__init__(disp)
|
|
|
|
@property
|
|
def screens(self):
|
|
screens = self.super_value("screens")
|
|
for i in range(self.screen_cnt):
|
|
yield LVObject(screens[i])
|
|
|
|
|
|
class LVGL:
|
|
"""LVGL instance"""
|
|
|
|
def __init__(self, lv_global: Value):
|
|
self.lv_global = lv_global.cast("lv_global_t", ptr=True)
|
|
|
|
def displays(self) -> Iterator[LVDisplay]:
|
|
ll = self.lv_global.disp_ll
|
|
if not ll:
|
|
return
|
|
|
|
for disp in LVList(ll, "lv_display_t"):
|
|
yield LVDisplay(disp)
|
|
|
|
def screen_active(self):
|
|
disp = self.lv_global.disp_default
|
|
return disp.act_scr if disp else None
|
|
|
|
def draw_units(self):
|
|
unit = self.lv_global.draw_info.unit_head
|
|
|
|
# Iterate through all draw units
|
|
while unit:
|
|
yield unit
|
|
unit = unit.next
|
|
|
|
|
|
def set_lvgl_instance(lv_global: Union[gdb.Value, Value, None]):
|
|
global g_lvgl_instance
|
|
|
|
if not lv_global:
|
|
try:
|
|
lv_global = Value(gdb.parse_and_eval("lv_global").address)
|
|
except gdb.error as e:
|
|
print(f"Failed to get lv_global: {e}")
|
|
return
|
|
|
|
if not isinstance(lv_global, Value):
|
|
lv_global = Value(lv_global)
|
|
|
|
inited = lv_global.inited
|
|
if not inited:
|
|
print("\x1b[31mlvgl is not initialized yet. Please call `set_lvgl_instance(None)` later.\x1b[0m")
|
|
return
|
|
|
|
g_lvgl_instance = LVGL(lv_global)
|
|
|
|
|
|
def dump_obj_info(obj: LVObject):
|
|
clzname = obj.class_name
|
|
coords = f"{obj.x1},{obj.y1},{obj.x2},{obj.y2}"
|
|
print(f"{clzname}@{hex(obj)} {coords}")
|
|
|
|
|
|
# Dump lv_style_const_prop_t
|
|
def dump_style_info(style: Value):
|
|
prop = int(style.prop)
|
|
value = style.value
|
|
print(f"{prop} = {value}")
|
|
|
|
|
|
class DumpObj(gdb.Command):
|
|
"""dump obj tree from specified obj"""
|
|
|
|
def __init__(self):
|
|
super(DumpObj, self).__init__(
|
|
"dump obj", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
|
)
|
|
|
|
def dump_obj(self, obj: LVObject, depth=0, limit=None):
|
|
if not obj:
|
|
return
|
|
|
|
# dump self
|
|
print(" " * depth, end="")
|
|
dump_obj_info(obj)
|
|
|
|
if limit is not None and depth >= limit:
|
|
return
|
|
|
|
# dump children
|
|
for child in obj.childs:
|
|
self.dump_obj(child, depth + 1, limit=limit)
|
|
|
|
def invoke(self, args, from_tty):
|
|
parser = argparse.ArgumentParser(description="Dump lvgl obj tree.")
|
|
parser.add_argument(
|
|
"-L",
|
|
"--level",
|
|
type=int,
|
|
default=None,
|
|
help="Limit the depth of the tree.",
|
|
)
|
|
parser.add_argument(
|
|
"root",
|
|
type=str,
|
|
nargs="?",
|
|
default=None,
|
|
help="Optional root obj to dump.",
|
|
)
|
|
try:
|
|
args = parser.parse_args(gdb.string_to_argv(args))
|
|
except SystemExit:
|
|
return
|
|
|
|
if args.root:
|
|
root = gdb.parse_and_eval(args.root)
|
|
root = LVObject(root)
|
|
self.dump_obj(root, limit=args.level)
|
|
else:
|
|
# dump all displays
|
|
depth = 0
|
|
for disp in g_lvgl_instance.displays():
|
|
print(f"Display {hex(disp)}")
|
|
for screen in disp.screens:
|
|
print(f'{" " * (depth + 1)}Screen@{hex(screen)}')
|
|
self.dump_obj(screen, depth=depth + 1, limit=args.level)
|
|
|
|
|
|
class InfoStyle(gdb.Command):
|
|
"""dump obj style value for specified obj"""
|
|
|
|
def __init__(self):
|
|
super(InfoStyle, self).__init__(
|
|
"info style", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
|
)
|
|
|
|
def invoke(self, args, from_tty):
|
|
parser = argparse.ArgumentParser(description="Dump lvgl obj local style.")
|
|
parser.add_argument(
|
|
"obj",
|
|
type=str,
|
|
help="obj to show style.",
|
|
)
|
|
|
|
try:
|
|
args = parser.parse_args(gdb.string_to_argv(args))
|
|
except SystemExit:
|
|
return
|
|
|
|
obj = gdb.parse_and_eval(args.obj)
|
|
if not obj:
|
|
print("Invalid obj: ", args.obj)
|
|
return
|
|
|
|
obj = Value(obj)
|
|
|
|
# show all styles applied to this obj
|
|
for style in LVObject(obj).styles:
|
|
print(" ", end="")
|
|
dump_style_info(style)
|
|
|
|
|
|
class InfoDrawUnit(gdb.Command):
|
|
"""dump draw unit info"""
|
|
|
|
def __init__(self):
|
|
super(InfoDrawUnit, self).__init__(
|
|
"info draw_unit", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
|
)
|
|
|
|
def dump_draw_unit(self, draw_unit: Value):
|
|
# Dereference to get the string content of the name from draw_unit
|
|
name = draw_unit.name.string()
|
|
|
|
# Print draw_unit information and the name
|
|
print(f"Draw Unit: {draw_unit}, Name: {name}")
|
|
|
|
# Handle different draw_units based on the name
|
|
def lookup_type(name):
|
|
try:
|
|
return gdb.lookup_type(name)
|
|
except gdb.error:
|
|
return None
|
|
|
|
types = {
|
|
"DMA2D": lookup_type("lv_draw_dma2d_unit_t"),
|
|
"NEMA_GFX": lookup_type("lv_draw_nema_gfx_unit_t"),
|
|
"NXP_PXP": lookup_type("lv_draw_pxp_unit_t"),
|
|
"NXP_VGLITE": lookup_type("lv_draw_vglite_unit_t"),
|
|
"OPENGLES": lookup_type("lv_draw_opengles_unit_t"),
|
|
"DAVE2D": lookup_type("lv_draw_dave2d_unit_t"),
|
|
"SDL": lookup_type("lv_draw_sdl_unit_t"),
|
|
"SW": lookup_type("lv_draw_sw_unit_t"),
|
|
"VG_LITE": lookup_type("lv_draw_vg_lite_unit_t"),
|
|
}
|
|
|
|
type = types.get(name, lookup_type("lv_draw_unit_t"))
|
|
print(draw_unit.cast(type, ptr=True).dereference().format_string(pretty_structs=True, symbols=True))
|
|
|
|
def invoke(self, args, from_tty):
|
|
for unit in g_lvgl_instance.draw_units():
|
|
self.dump_draw_unit(unit)
|