mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-09 20:27:41 +08:00
chore(gdb): add LVFsDrv wrapper with driver name resolution and dump fs_drv command
This commit is contained in:
@@ -36,6 +36,7 @@ from .lvgl import (
|
||||
EVENT_CODE_NAMES,
|
||||
LVAnim,
|
||||
LVTimer,
|
||||
LVFsDrv,
|
||||
)
|
||||
from . import cmds as cmds
|
||||
|
||||
@@ -75,4 +76,5 @@ __all__ = [
|
||||
"EVENT_CODE_NAMES",
|
||||
"LVAnim",
|
||||
"LVTimer",
|
||||
"LVFsDrv",
|
||||
]
|
||||
|
||||
@@ -10,6 +10,7 @@ from .misc import (
|
||||
CheckCache,
|
||||
DumpAnim,
|
||||
DumpTimer,
|
||||
DumpFsDrv,
|
||||
)
|
||||
from .debugger import Debugger
|
||||
from .drivers import Lvglobal
|
||||
@@ -34,6 +35,7 @@ CheckPrefix()
|
||||
CheckCache()
|
||||
DumpAnim()
|
||||
DumpTimer()
|
||||
DumpFsDrv()
|
||||
DumpDrawTask()
|
||||
|
||||
# Infos
|
||||
|
||||
@@ -2,6 +2,7 @@ from .lv_style import InfoStyle
|
||||
from .lv_cache import DumpCache, CheckPrefix, CheckCache
|
||||
from .lv_anim import DumpAnim
|
||||
from .lv_timer import DumpTimer
|
||||
from .lv_fs import DumpFsDrv
|
||||
|
||||
__all__ = [
|
||||
"InfoStyle",
|
||||
@@ -10,4 +11,5 @@ __all__ = [
|
||||
"CheckCache",
|
||||
"DumpAnim",
|
||||
"DumpTimer",
|
||||
"DumpFsDrv",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import gdb
|
||||
|
||||
from lvglgdb.lvgl import curr_inst
|
||||
from lvglgdb.lvgl.misc.lv_fs import LVFsDrv
|
||||
|
||||
|
||||
class DumpFsDrv(gdb.Command):
|
||||
"""dump all registered filesystem drivers"""
|
||||
|
||||
def __init__(self):
|
||||
super(DumpFsDrv, self).__init__(
|
||||
"dump fs_drv", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
||||
)
|
||||
|
||||
def invoke(self, args, from_tty):
|
||||
LVFsDrv.print_entries(curr_inst().fs_drivers())
|
||||
@@ -37,6 +37,7 @@ from .misc import (
|
||||
EVENT_CODE_NAMES,
|
||||
LVAnim,
|
||||
LVTimer,
|
||||
LVFsDrv,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
@@ -80,4 +81,5 @@ __all__ = [
|
||||
"EVENT_CODE_NAMES",
|
||||
"LVAnim",
|
||||
"LVTimer",
|
||||
"LVFsDrv",
|
||||
]
|
||||
|
||||
@@ -73,7 +73,13 @@ class LVGL:
|
||||
for timer in LVList(self.lv_global.timer_state.timer_ll, "lv_timer_t"):
|
||||
yield LVTimer(timer)
|
||||
|
||||
def fs_drivers(self):
|
||||
from ..misc.lv_fs import LVFsDrv
|
||||
|
||||
# fsdrv_ll stores lv_fs_drv_t* pointers (not inline structs)
|
||||
pp_type = gdb.lookup_type("lv_fs_drv_t").pointer().pointer()
|
||||
for drv_pp in LVList(self.lv_global.fsdrv_ll, pp_type):
|
||||
yield LVFsDrv(drv_pp.dereference())
|
||||
|
||||
def image_header_cache(self):
|
||||
from ..misc.lv_image_header_cache import LVImageHeaderCache
|
||||
|
||||
@@ -25,6 +25,7 @@ from .lv_event import (
|
||||
)
|
||||
from .lv_anim import LVAnim
|
||||
from .lv_timer import LVTimer
|
||||
from .lv_fs import LVFsDrv
|
||||
|
||||
__all__ = [
|
||||
"LVList",
|
||||
@@ -55,4 +56,5 @@ __all__ = [
|
||||
"EVENT_CODE_NAMES",
|
||||
"LVAnim",
|
||||
"LVTimer",
|
||||
"LVFsDrv",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
from prettytable import PrettyTable
|
||||
|
||||
from lvglgdb.value import Value, ValueInput
|
||||
from .lv_utils import resolve_source_name, build_global_field_map
|
||||
|
||||
|
||||
class LVFsDrv(Value):
|
||||
"""LVGL filesystem driver wrapper"""
|
||||
|
||||
def __init__(self, drv: ValueInput):
|
||||
super().__init__(Value.normalize(drv, "lv_fs_drv_t"))
|
||||
|
||||
@property
|
||||
def letter(self) -> str:
|
||||
return chr(int(self.super_value("letter")))
|
||||
|
||||
# Lazily built address -> name map shared across all instances
|
||||
_addr_map = None
|
||||
|
||||
@property
|
||||
def driver_name(self) -> str:
|
||||
"""Infer driver name from lv_global_t field or open_cb source file."""
|
||||
if LVFsDrv._addr_map is None:
|
||||
m = build_global_field_map("lv_fs_drv_t")
|
||||
LVFsDrv._addr_map = {
|
||||
addr: name.replace("_fs_drv", "") for addr, name in m.items()
|
||||
}
|
||||
# Try lv_global_t field match first (no debug info needed)
|
||||
name = LVFsDrv._addr_map.get(int(self), None)
|
||||
if name:
|
||||
return name
|
||||
# Fallback: resolve from open_cb source file (needs debug info)
|
||||
return resolve_source_name(int(self.open_cb), prefix="lv_fs_") or "unknown"
|
||||
|
||||
@property
|
||||
def cache_size(self) -> int:
|
||||
return int(self.super_value("cache_size"))
|
||||
|
||||
@property
|
||||
def ready_cb(self) -> Value:
|
||||
return self.super_value("ready_cb")
|
||||
|
||||
@property
|
||||
def open_cb(self) -> Value:
|
||||
return self.super_value("open_cb")
|
||||
|
||||
@property
|
||||
def close_cb(self) -> Value:
|
||||
return self.super_value("close_cb")
|
||||
|
||||
@property
|
||||
def read_cb(self) -> Value:
|
||||
return self.super_value("read_cb")
|
||||
|
||||
@property
|
||||
def write_cb(self) -> Value:
|
||||
return self.super_value("write_cb")
|
||||
|
||||
@property
|
||||
def seek_cb(self) -> Value:
|
||||
return self.super_value("seek_cb")
|
||||
|
||||
@property
|
||||
def tell_cb(self) -> Value:
|
||||
return self.super_value("tell_cb")
|
||||
|
||||
@property
|
||||
def dir_open_cb(self) -> Value:
|
||||
return self.super_value("dir_open_cb")
|
||||
|
||||
@property
|
||||
def dir_read_cb(self) -> Value:
|
||||
return self.super_value("dir_read_cb")
|
||||
|
||||
@property
|
||||
def dir_close_cb(self) -> Value:
|
||||
return self.super_value("dir_close_cb")
|
||||
|
||||
@property
|
||||
def user_data(self) -> Value:
|
||||
return self.super_value("user_data")
|
||||
|
||||
@staticmethod
|
||||
def _fmt_cb(cb: Value) -> str:
|
||||
addr = int(cb)
|
||||
if not addr:
|
||||
return "-"
|
||||
return cb.format_string(symbols=True).replace("\x00", "")
|
||||
|
||||
@staticmethod
|
||||
def print_entries(drivers):
|
||||
"""Print filesystem drivers as a PrettyTable."""
|
||||
table = PrettyTable()
|
||||
table.field_names = [
|
||||
"#",
|
||||
"letter",
|
||||
"type",
|
||||
"cache_size",
|
||||
"open_cb",
|
||||
"read_cb",
|
||||
"write_cb",
|
||||
"close_cb",
|
||||
]
|
||||
table.align = "l"
|
||||
|
||||
fmt = LVFsDrv._fmt_cb
|
||||
for i, drv in enumerate(drivers):
|
||||
table.add_row(
|
||||
[
|
||||
i,
|
||||
f"{drv.letter}:",
|
||||
drv.driver_name,
|
||||
drv.cache_size,
|
||||
fmt(drv.open_cb),
|
||||
fmt(drv.read_cb),
|
||||
fmt(drv.write_cb),
|
||||
fmt(drv.close_cb),
|
||||
]
|
||||
)
|
||||
|
||||
if not table.rows:
|
||||
print("No registered filesystem drivers.")
|
||||
else:
|
||||
print(str(table).replace("\x00", ""))
|
||||
@@ -0,0 +1,67 @@
|
||||
import os
|
||||
|
||||
import gdb
|
||||
|
||||
|
||||
def resolve_source_name(addr, prefix="lv_"):
|
||||
"""Resolve a code address to a short name derived from its source file.
|
||||
|
||||
Tries block_for_pc first, then find_pc_line as fallback.
|
||||
Strips common prefixes (e.g. "lv_fs_stdio.c" -> "stdio").
|
||||
|
||||
Args:
|
||||
addr: Integer address of a function/callback.
|
||||
prefix: Prefix to strip from filename (default "lv_").
|
||||
|
||||
Returns:
|
||||
Short name string, or None if resolution fails.
|
||||
"""
|
||||
if not addr:
|
||||
return None
|
||||
fname = None
|
||||
try:
|
||||
block = gdb.block_for_pc(addr)
|
||||
if block and block.function and block.function.symtab:
|
||||
fname = block.function.symtab.filename
|
||||
except (gdb.error, RuntimeError):
|
||||
pass
|
||||
if not fname:
|
||||
try:
|
||||
sal = gdb.find_pc_line(addr)
|
||||
if sal and sal.symtab:
|
||||
fname = sal.symtab.filename
|
||||
except gdb.error:
|
||||
pass
|
||||
if not fname:
|
||||
return None
|
||||
base = os.path.basename(fname).replace(".c", "")
|
||||
if prefix and base.startswith(prefix):
|
||||
base = base[len(prefix) :]
|
||||
return base
|
||||
|
||||
|
||||
def build_global_field_map(field_type_name):
|
||||
"""Build address -> field name map for fields of a given type in lv_global_t.
|
||||
|
||||
Scans lv_global_t struct fields, finds those matching the given type,
|
||||
and returns a dict mapping their runtime addresses to field names.
|
||||
|
||||
Args:
|
||||
field_type_name: C type name string (e.g. "lv_fs_drv_t").
|
||||
|
||||
Returns:
|
||||
Dict mapping int address -> str field name.
|
||||
"""
|
||||
try:
|
||||
lv_global_val = gdb.parse_and_eval("lv_global")
|
||||
lv_global_addr = int(lv_global_val.address)
|
||||
lv_global_type = gdb.lookup_type("lv_global_t")
|
||||
target_type = gdb.lookup_type(field_type_name)
|
||||
result = {}
|
||||
for field in lv_global_type.fields():
|
||||
if field.type.strip_typedefs() == target_type:
|
||||
addr = lv_global_addr + field.bitpos // 8
|
||||
result[addr] = field.name
|
||||
return result
|
||||
except gdb.error:
|
||||
return {}
|
||||
Reference in New Issue
Block a user