First commit with working display

This commit is contained in:
Ricardo LAP
2026-09-13 20:50:51 +00:00
parent a83af259b3
commit c54c68c899
6690 changed files with 1093387 additions and 2 deletions

View File

@@ -0,0 +1,32 @@
import gdb
from .core import DumpObj
from .display import DumpDisplayBuf
from .draw import InfoDrawUnit
from .misc import InfoStyle, DumpCache
from .debugger import Debugger
from .drivers import Lvglobal
__all__ = []
# Set pagination off and python print-stack full
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")
# Debugger
Debugger()
# Dumps
DumpObj()
DumpDisplayBuf()
DumpCache()
# Infos
InfoStyle()
InfoDrawUnit()
# Drivers
Lvglobal()

View File

@@ -0,0 +1,5 @@
from .lv_obj import DumpObj
__all__ = [
"DumpObj",
]

View File

@@ -0,0 +1,64 @@
import argparse
import gdb
from lvglgdb.lvgl import curr_inst
from lvglgdb.lvgl import LVObject, dump_obj_info
from lvglgdb.value import 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.children:
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(Value(root))
self.dump_obj(root, limit=args.level)
else:
# dump all displays
depth = 0
for disp in curr_inst().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)

View File

@@ -0,0 +1,75 @@
import argparse
import gdb
class Debugger(gdb.Command):
"""Start debugpy server or connect to a debug server, so we can debug python code from IDE like PyCharm/VSCode"""
def __init__(self, host="localhost", port=11451):
self.__host = host
self.__port = port
super().__init__("debugger", gdb.COMMAND_USER)
def invoke(self, args, from_tty):
parser = argparse.ArgumentParser(description=Debugger.__doc__)
parser.add_argument(
"--host",
type=str,
help="Server listening host",
)
parser.add_argument(
"-p",
"--port",
type=int,
help="Server listening port",
)
parser.add_argument(
"-t",
"--type",
choices=["pycharm", "vscode", "eclipse"],
help="Debugger type",
required=True,
)
try:
args = parser.parse_args(gdb.string_to_argv(args))
except SystemExit:
return
if args.host:
self.__host = args.host
if args.port:
self.__port = args.port
if args.type == "pycharm":
self.connect_to_pycharm()
elif args.type == "vscode":
self.connect_to_vscode()
elif args.type == "eclipse":
self.connect_to_eclipse()
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):
try:
import debugpy
except ImportError:
print("debugpy module not found. Please install it using pip.")
return
debugpy.listen((self.__host, self.__port))
debugpy.wait_for_client()
def connect_to_eclipse(self):
print("Eclipse is not implemented yet")

View File

@@ -0,0 +1,5 @@
from .lv_display import DumpDisplayBuf
__all__ = [
"DumpDisplayBuf",
]

View File

@@ -0,0 +1,54 @@
import argparse
import gdb
from lvglgdb.lvgl import curr_inst
from lvglgdb.lvgl import LVDrawBuf
class DumpDisplayBuf(gdb.Command):
"""dump display buf to image"""
def __init__(self):
super(DumpDisplayBuf, self).__init__(
"dump display", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
)
def invoke(self, args, from_tty):
parser = argparse.ArgumentParser(description="Dump display draw buffer.")
parser.add_argument(
"-p",
"--prefix",
type=str,
default="",
help="prefix of dump file path",
)
parser.add_argument(
"-f",
"--format",
type=str,
choices=["bmp", "png"],
default=None,
help="dump file format (bmp or png)",
)
try:
args = parser.parse_args(gdb.string_to_argv(args))
except SystemExit:
return
display = curr_inst().disp_default()
if not display:
print("Error: Invalid display pointer")
return
buffers = {
"buf_1": display.buf_1,
"buf_2": display.buf_2,
}
for buf_name, buf_ptr in buffers.items():
if buf_ptr is not None:
draw_buf = LVDrawBuf(buf_ptr)
filename = f"{args.prefix}{buf_name}.{args.format.lower() if args.format else 'bmp'}"
draw_buf.data_dump(filename, args.format)
else:
print(f"Warning: {buf_name} buffer is None, skipping.")

View File

@@ -0,0 +1,5 @@
from .lv_draw import InfoDrawUnit
__all__ = [
"InfoDrawUnit",
]

View File

@@ -0,0 +1,50 @@
import gdb
from lvglgdb.value import Value
from lvglgdb.lvgl import curr_inst
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 curr_inst().draw_units():
self.dump_draw_unit(unit)

View File

@@ -0,0 +1,5 @@
from .nuttx import Lvglobal
__all__ = [
"Lvglobal",
]

View File

@@ -0,0 +1,5 @@
from .nuttx import Lvglobal
__all__ = [
"Lvglobal",
]

View File

@@ -0,0 +1,55 @@
# GDB script to get lvgl global pointer in NuttX.
import argparse
import sys
import gdb
class Lvglobal(gdb.Command):
"""Set which lvgl instance to inspect by finding lv_global pointer in task TLS data."""
def __init__(self):
super(Lvglobal, self).__init__("lvglobal", gdb.COMMAND_USER)
def set_lvgl_instance(self, inst):
from lvglgdb.lvgl import curr_inst
curr_inst().reset()
curr_inst().ensure_init(inst)
def invoke(self, arg, from_tty):
parser = argparse.ArgumentParser(description=self.__doc__)
parser.add_argument("-p", "--pid", type=int, help="Optional process ID")
try:
args = parser.parse_args(gdb.string_to_argv(arg))
except SystemExit:
return
try:
from nxgdb import utils
except ImportError:
print("nxgdb is not installed, can't find lvgl global pointer.")
return
lv_global = utils.gdb_eval_or_none("lv_global")
if lv_global:
print(f"Found single instance lv_global@{hex(lv_global.address)}")
self.set_lvgl_instance(lv_global)
return
# find the lvgl global pointer in tls
if not args.pid:
print("LVGL is in multi-process mode; please provide --pid.")
return
lv_key = utils.gdb_eval_or_none("lv_nuttx_tlskey")
if lv_key is None:
lv_key = utils.gdb_eval_or_none("lv_global_default::index")
if lv_key is None:
print("Can't find lvgl tls key in multi-process mode.")
return
lv_global = utils.get_task_tls(args.pid, lv_key)
if lv_global:
print(f"Found lv_global@{hex(lv_global)}")
self.set_lvgl_instance(lv_global)
else:
print(f"\nCan't find lv_global with tlskey@{hex(lv_key)}.")

View File

@@ -0,0 +1,4 @@
from .lv_style import InfoStyle
from .lv_cache import DumpCache
__all__ = ["InfoStyle", "DumpCache"]

View File

@@ -0,0 +1,40 @@
import argparse
import gdb
class DumpCache(gdb.Command):
"""dump cache info for specified cache"""
def __init__(self):
super(DumpCache, self).__init__(
"dump cache", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
)
def invoke(self, args, from_tty):
parser = argparse.ArgumentParser(description="Dump lvgl cache info.")
parser.add_argument(
"cache",
type=str,
choices=["image", "image_header"],
default="image",
help="cache to dump.",
)
from lvglgdb import curr_inst
try:
args = parser.parse_args(gdb.string_to_argv(args))
except SystemExit:
return
cache = None
if args.cache == "image":
cache = curr_inst().image_cache()
elif args.cache == "image_header":
cache = curr_inst().image_header_cache()
if not cache:
print("Invalid cache: ", args.cache)
return
cache.print_entries()

View File

@@ -0,0 +1,40 @@
import argparse
import gdb
from lvglgdb.value import Value
from lvglgdb.lvgl import LVObject
from lvglgdb.lvgl import dump_style_info
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)