mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-30 15:17:33 +08:00
refactor(gdb): refactor gdb script (#7123)
Signed-off-by: Benign X <1341398182@qq.com> Co-authored-by: Neo Xu <neo.xu1990@gmail.com>
This commit is contained in:
@@ -15,14 +15,14 @@ if your device crashes and you have a core dump, you can use GDB to analyze the
|
|||||||
core dump. To load the LVGL GDB plugin within GDB's command line, type the
|
core dump. To load the LVGL GDB plugin within GDB's command line, type the
|
||||||
following command:
|
following command:
|
||||||
|
|
||||||
``source lvgl/scripts/gdb/lvgl.py``
|
``source lvgl/scripts/gdb/gdbinit.py``
|
||||||
|
|
||||||
|
|
||||||
Example of usage:
|
Example of usage:
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
(gdb) source lvgl/scripts/gdb/lvgl.py
|
(gdb) source lvgl/scripts/gdb/gdbinit.py
|
||||||
|
|
||||||
(gdb) dump obj -L 2
|
(gdb) dump obj -L 2
|
||||||
obj@0x60700000dd10 (0,0,799,599)
|
obj@0x60700000dd10 (0,0,799,599)
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import sys
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
here = path.dirname(path.abspath(__file__))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if here not in sys.path:
|
||||||
|
sys.path.insert(0, here)
|
||||||
|
|
||||||
|
for key in tuple(filter(lambda m: m.startswith("lvglgdb"), sys.modules.keys())):
|
||||||
|
del sys.modules[key]
|
||||||
|
|
||||||
|
import lvglgdb # noqa: F401
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from .lvgl import *
|
||||||
|
from .value import *
|
||||||
|
|
||||||
|
DumpObj()
|
||||||
|
InfoStyle()
|
||||||
|
InfoDrawUnit()
|
||||||
|
set_lvgl_instance(None)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
class Debugger(object):
|
||||||
|
def __init__(self, host="localhost", port=11451):
|
||||||
|
self.__host = host
|
||||||
|
self.__port = port
|
||||||
|
|
||||||
|
def connect_to_pycharm(self):
|
||||||
|
try:
|
||||||
|
import pydevd_pycharm
|
||||||
|
except ImportError:
|
||||||
|
print("pydevd_pycharm module not found. Please install it using pip.")
|
||||||
|
return
|
||||||
|
|
||||||
|
pydevd_pycharm.settrace(self.__host, port=self.__port, stdoutToServer=True, stderrToServer=True)
|
||||||
|
|
||||||
|
def connect_to_vscode(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def connect_to_eclipse(self):
|
||||||
|
raise NotImplementedError()
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
# Usage: source lvgl.py
|
|
||||||
import argparse
|
import argparse
|
||||||
from typing import Iterator, Union
|
from typing import Iterator, Union, Optional
|
||||||
|
|
||||||
import gdb
|
import gdb
|
||||||
|
|
||||||
|
from .value import Value
|
||||||
|
|
||||||
gdb.execute("set pagination off")
|
gdb.execute("set pagination off")
|
||||||
gdb.write("set pagination off\n")
|
gdb.write("set pagination off\n")
|
||||||
gdb.execute("set python print-stack full")
|
gdb.execute("set python print-stack full")
|
||||||
@@ -12,31 +13,31 @@ gdb.write("set python print-stack full\n")
|
|||||||
g_lvgl_instance = None
|
g_lvgl_instance = None
|
||||||
|
|
||||||
|
|
||||||
class LVList:
|
class LVList(Value):
|
||||||
"""LVGL linked list iterator"""
|
"""LVGL linked list iterator"""
|
||||||
|
|
||||||
def __init__(self, ll: gdb.Value, nodetype: Union[gdb.Type, str] = None):
|
def __init__(self, ll: Value, nodetype: Union[gdb.Type, str] = None):
|
||||||
if not ll:
|
if not ll:
|
||||||
raise ValueError("Invalid linked list")
|
raise ValueError("Invalid linked list")
|
||||||
|
super().__init__(ll)
|
||||||
|
|
||||||
self.ll = ll
|
|
||||||
self.nodetype = (
|
self.nodetype = (
|
||||||
gdb.lookup_type(nodetype).pointer()
|
gdb.lookup_type(nodetype).pointer()
|
||||||
if isinstance(nodetype, str)
|
if isinstance(nodetype, str)
|
||||||
else nodetype
|
else nodetype
|
||||||
)
|
)
|
||||||
self.lv_ll_node_t = gdb.lookup_type("lv_ll_node_t").pointer()
|
self.lv_ll_node_t = gdb.lookup_type("lv_ll_node_t").pointer()
|
||||||
self.current = ll["head"]
|
self.current = self.head
|
||||||
self._next_offset = ll["n_size"] + self.lv_ll_node_t.sizeof
|
self._next_offset = self.n_size + self.lv_ll_node_t.sizeof
|
||||||
self._prev_offset = ll["n_size"]
|
self._prev_offset = self.n_size
|
||||||
|
|
||||||
def _next(self, node):
|
def _next(self, node):
|
||||||
next = gdb.Value(int(node) + self._next_offset)
|
next_value = Value(int(node) + self._next_offset)
|
||||||
return next.cast(self.lv_ll_node_t.pointer()).dereference()
|
return next_value.cast(self.lv_ll_node_t, ptr=True).dereference()
|
||||||
|
|
||||||
def _prev(self, node):
|
def _prev(self, node):
|
||||||
prev = gdb.Value(int(node) + self._prev_offset)
|
prev_value = Value(int(node) + self._prev_offset)
|
||||||
return prev.cast(self.lv_ll_node_t)
|
return prev_value.cast(self.lv_ll_node_t, ptr=True).dereference()
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self
|
return self
|
||||||
@@ -54,130 +55,99 @@ class LVList:
|
|||||||
@property
|
@property
|
||||||
def len(self):
|
def len(self):
|
||||||
len = 0
|
len = 0
|
||||||
node = self.ll["head"]
|
node = self.head
|
||||||
while node:
|
while node:
|
||||||
len += 1
|
len += 1
|
||||||
node = self._next(node)
|
node = self._next(node)
|
||||||
return len
|
return len
|
||||||
|
|
||||||
@property
|
|
||||||
def head(self):
|
|
||||||
return self.ll["head"]
|
|
||||||
|
|
||||||
@property
|
class LVObject(Value):
|
||||||
def tail(self):
|
|
||||||
return self.ll["tail"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def size(self):
|
|
||||||
return self.ll["n_size"]
|
|
||||||
|
|
||||||
|
|
||||||
class LVObject(gdb.Value):
|
|
||||||
"""LVGL object"""
|
"""LVGL object"""
|
||||||
|
|
||||||
def __init__(self, obj: gdb.Value):
|
def __init__(self, obj: Value):
|
||||||
super().__init__(obj)
|
super().__init__(obj.cast("lv_obj_t", ptr=True))
|
||||||
self.obj = obj
|
|
||||||
|
|
||||||
@property
|
|
||||||
def class_p(self):
|
|
||||||
return self.obj["class_p"]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def class_name(self):
|
def class_name(self):
|
||||||
name = self.class_p["name"]
|
name = self.class_p.name
|
||||||
return name.string() if name else "unknown"
|
return name.string() if name else "unknown"
|
||||||
|
|
||||||
@property
|
|
||||||
def coords(self):
|
|
||||||
return self.obj["coords"]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def x1(self):
|
def x1(self):
|
||||||
return int(self.coords["x1"])
|
return int(self.coords.x1)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def y1(self):
|
def y1(self):
|
||||||
return int(self.coords["y1"])
|
return int(self.coords.y1)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def x2(self):
|
def x2(self):
|
||||||
return int(self.coords["x2"])
|
return int(self.coords.x2)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def y2(self):
|
def y2(self):
|
||||||
return int(self.coords["y2"])
|
return int(self.coords.y2)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def child_count(self):
|
def child_count(self):
|
||||||
return self.obj["spec_attr"]["child_cnt"] if self.obj["spec_attr"] else 0
|
return self.spec_attr.child_cnt if self.spec_attr else 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def childs(self):
|
def childs(self):
|
||||||
if not self.obj["spec_attr"]:
|
if not self.spec_attr:
|
||||||
return
|
return
|
||||||
|
|
||||||
for i in range(self.child_count):
|
for i in range(self.child_count):
|
||||||
child = self.obj["spec_attr"]["children"][i]
|
child = self.spec_attr.children[i]
|
||||||
yield LVObject(child)
|
yield LVObject(child)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def styles(self):
|
def styles(self):
|
||||||
LV_STYLE_PROP_INV = 0
|
LV_STYLE_PROP_INV = 0
|
||||||
LV_STYLE_PROP_ANY = 0xFF
|
LV_STYLE_PROP_ANY = 0xFF
|
||||||
count = self.obj["style_cnt"]
|
count = self.style_cnt
|
||||||
if count == 0:
|
if count == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
styles = self.obj["styles"]
|
styles = self.super_value("styles")
|
||||||
for i in range(count):
|
for i in range(count):
|
||||||
style = styles[i]["style"]
|
style = styles[i].style
|
||||||
prop_cnt = style["prop_cnt"]
|
prop_cnt = style.prop_cnt
|
||||||
values_and_props = style["values_and_props"].cast(
|
values_and_props = style.values_and_props.cast("lv_style_const_prop_t", ptr=True)
|
||||||
gdb.lookup_type("lv_style_const_prop_t").pointer()
|
|
||||||
)
|
|
||||||
for j in range(prop_cnt):
|
for j in range(prop_cnt):
|
||||||
prop = values_and_props[j]["prop"]
|
prop = values_and_props[j].prop
|
||||||
if prop == LV_STYLE_PROP_INV or prop == LV_STYLE_PROP_ANY:
|
if prop == LV_STYLE_PROP_INV or prop == LV_STYLE_PROP_ANY:
|
||||||
continue
|
continue
|
||||||
yield values_and_props[j]
|
yield values_and_props[j]
|
||||||
|
|
||||||
def get_child(self, index: int):
|
def get_child(self, index: int):
|
||||||
return (
|
return (
|
||||||
self.obj["spec_attr"]["children"][index] if self.obj["spec_attr"] else None
|
self.spec_attr.children[index] if self.spec_attr else None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class LVDisplay(gdb.Value):
|
class LVDisplay(Value):
|
||||||
"""LVGL display"""
|
"""LVGL display"""
|
||||||
|
|
||||||
def __init__(self, disp: gdb.Value):
|
def __init__(self, disp: Value):
|
||||||
super().__init__(disp)
|
super().__init__(disp)
|
||||||
self.disp = disp
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def screens(self):
|
def screens(self):
|
||||||
|
screens = self.super_value("screens")
|
||||||
for i in range(self.screen_cnt):
|
for i in range(self.screen_cnt):
|
||||||
yield LVObject(self.disp["screens"][i])
|
yield LVObject(screens[i])
|
||||||
|
|
||||||
@property
|
|
||||||
def screen_cnt(self):
|
|
||||||
return self.disp["screen_cnt"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def act_scr(self):
|
|
||||||
return self.disp["act_scr"]
|
|
||||||
|
|
||||||
|
|
||||||
class LVGL:
|
class LVGL:
|
||||||
"""LVGL instance"""
|
"""LVGL instance"""
|
||||||
|
|
||||||
def __init__(self, lv_global: gdb.Value):
|
def __init__(self, lv_global: Value):
|
||||||
self.lv_global = lv_global.cast(gdb.lookup_type("lv_global_t").pointer())
|
self.lv_global = lv_global.cast("lv_global_t", ptr=True)
|
||||||
|
|
||||||
def displays(self) -> Iterator[LVDisplay]:
|
def displays(self) -> Iterator[LVDisplay]:
|
||||||
ll = self.lv_global["disp_ll"]
|
ll = self.lv_global.disp_ll
|
||||||
if not ll:
|
if not ll:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -185,27 +155,36 @@ class LVGL:
|
|||||||
yield LVDisplay(disp)
|
yield LVDisplay(disp)
|
||||||
|
|
||||||
def screen_active(self):
|
def screen_active(self):
|
||||||
disp = self.lv_global["disp_default"]
|
disp = self.lv_global.disp_default
|
||||||
return disp["act_scr"] if disp else None
|
return disp.act_scr if disp else None
|
||||||
|
|
||||||
def draw_units(self):
|
def draw_units(self):
|
||||||
unit = self.lv_global["draw_info"]["unit_head"]
|
unit = self.lv_global.draw_info.unit_head
|
||||||
|
|
||||||
# Iterate through all draw units
|
# Iterate through all draw units
|
||||||
while unit:
|
while unit:
|
||||||
yield unit
|
yield unit
|
||||||
unit = unit["next"]
|
unit = unit.next
|
||||||
|
|
||||||
def set_lvgl_instance(lv_global: gdb.Value):
|
|
||||||
|
def set_lvgl_instance(lv_global: Union[gdb.Value, Value, None]):
|
||||||
global g_lvgl_instance
|
global g_lvgl_instance
|
||||||
|
|
||||||
if not lv_global:
|
if not lv_global:
|
||||||
try:
|
try:
|
||||||
lv_global = gdb.parse_and_eval("lv_global").address
|
lv_global = Value(gdb.parse_and_eval("lv_global").address)
|
||||||
except gdb.error as e:
|
except gdb.error as e:
|
||||||
print(f"Failed to get lv_global: {e}")
|
print(f"Failed to get lv_global: {e}")
|
||||||
return
|
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)
|
g_lvgl_instance = LVGL(lv_global)
|
||||||
|
|
||||||
|
|
||||||
@@ -216,9 +195,9 @@ def dump_obj_info(obj: LVObject):
|
|||||||
|
|
||||||
|
|
||||||
# Dump lv_style_const_prop_t
|
# Dump lv_style_const_prop_t
|
||||||
def dump_style_info(style: gdb.Value):
|
def dump_style_info(style: Value):
|
||||||
prop = int(style["prop"])
|
prop = int(style.prop)
|
||||||
value = style["value"]
|
value = style.value
|
||||||
print(f"{prop} = {value}")
|
print(f"{prop} = {value}")
|
||||||
|
|
||||||
|
|
||||||
@@ -306,13 +285,14 @@ class InfoStyle(gdb.Command):
|
|||||||
print("Invalid obj: ", args.obj)
|
print("Invalid obj: ", args.obj)
|
||||||
return
|
return
|
||||||
|
|
||||||
obj = obj.cast(gdb.lookup_type("lv_obj_t").pointer())
|
obj = Value(obj)
|
||||||
|
|
||||||
# show all styles applied to this obj
|
# show all styles applied to this obj
|
||||||
for style in LVObject(obj).styles:
|
for style in LVObject(obj).styles:
|
||||||
print(" ", end="")
|
print(" ", end="")
|
||||||
dump_style_info(style)
|
dump_style_info(style)
|
||||||
|
|
||||||
|
|
||||||
class InfoDrawUnit(gdb.Command):
|
class InfoDrawUnit(gdb.Command):
|
||||||
"""dump draw unit info"""
|
"""dump draw unit info"""
|
||||||
|
|
||||||
@@ -321,9 +301,9 @@ class InfoDrawUnit(gdb.Command):
|
|||||||
"info draw_unit", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
"info draw_unit", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
||||||
)
|
)
|
||||||
|
|
||||||
def dump_draw_unit(self, draw_unit:gdb.Value):
|
def dump_draw_unit(self, draw_unit: Value):
|
||||||
# Dereference to get the string content of the name from draw_unit
|
# Dereference to get the string content of the name from draw_unit
|
||||||
name = draw_unit["name"].string()
|
name = draw_unit.name.string()
|
||||||
|
|
||||||
# Print draw_unit information and the name
|
# Print draw_unit information and the name
|
||||||
print(f"Draw Unit: {draw_unit}, Name: {name}")
|
print(f"Draw Unit: {draw_unit}, Name: {name}")
|
||||||
@@ -347,14 +327,9 @@ class InfoDrawUnit(gdb.Command):
|
|||||||
"VG_LITE": lookup_type("lv_draw_vg_lite_unit_t"),
|
"VG_LITE": lookup_type("lv_draw_vg_lite_unit_t"),
|
||||||
}
|
}
|
||||||
|
|
||||||
type = types.get(name) or lookup_type("lv_draw_unit_t")
|
type = types.get(name, lookup_type("lv_draw_unit_t"))
|
||||||
print(draw_unit.cast(type.pointer()).dereference().format_string(pretty_structs=True, symbols=True))
|
print(draw_unit.cast(type, ptr=True).dereference().format_string(pretty_structs=True, symbols=True))
|
||||||
|
|
||||||
def invoke(self, args, from_tty):
|
def invoke(self, args, from_tty):
|
||||||
for unit in g_lvgl_instance.draw_units():
|
for unit in g_lvgl_instance.draw_units():
|
||||||
self.dump_draw_unit(unit)
|
self.dump_draw_unit(unit)
|
||||||
|
|
||||||
DumpObj()
|
|
||||||
InfoStyle()
|
|
||||||
InfoDrawUnit()
|
|
||||||
set_lvgl_instance(None)
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import gdb
|
||||||
|
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
|
||||||
|
class Value(gdb.Value):
|
||||||
|
def __init__(self, value: Union[gdb.Value, 'Value']):
|
||||||
|
super().__init__(value)
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
try:
|
||||||
|
value = super().__getitem__(key)
|
||||||
|
except gdb.error:
|
||||||
|
value = super().__getattr__(key)
|
||||||
|
return Value(value)
|
||||||
|
|
||||||
|
def __getattr__(self, key):
|
||||||
|
if hasattr(super(), key):
|
||||||
|
return Value(super().__getattribute__(key))
|
||||||
|
return Value(super().__getitem__(key))
|
||||||
|
|
||||||
|
def cast(self, type_name: str | gdb.Type, ptr: bool = False) -> Optional['Value']:
|
||||||
|
try:
|
||||||
|
gdb_type = gdb.lookup_type(type_name) if isinstance(type_name, str) else type_name
|
||||||
|
if ptr:
|
||||||
|
gdb_type = gdb_type.pointer()
|
||||||
|
return Value(super().cast(gdb_type))
|
||||||
|
except gdb.error:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def super_value(self, attr: str) -> 'Value':
|
||||||
|
return self[attr]
|
||||||
Reference in New Issue
Block a user