chore(gdb): support rb/cache associate debug obj and commands (#9089)

This commit is contained in:
Benign X
2025-10-28 15:54:45 +08:00
committed by GitHub
parent 3175239180
commit 6fe1a9224e
19 changed files with 1083 additions and 9 deletions
+26 -1
View File
@@ -1,5 +1,21 @@
from .value import Value
from .lvgl import curr_inst, LVDisplay, LVDrawBuf, LVList, LVObject, dump_style_info
from .lvgl import (
curr_inst,
LVDisplay,
LVDrawBuf,
LVList,
LVObject,
dump_style_info,
LVCache,
LVCacheEntry,
LVCacheLRURB,
LVCacheLRURBIterator,
LVCacheIteratorBase,
LVImageCache,
LVImageHeaderCache,
create_cache_iterator,
LVRedBlackTree,
)
from . import cmds as cmds
__all__ = [
@@ -7,7 +23,16 @@ __all__ = [
"LVDisplay",
"LVDrawBuf",
"LVList",
"LVCache",
"LVRedBlackTree",
"LVObject",
"dump_style_info",
"Value",
"LVCacheEntry",
"LVCacheLRURB",
"LVCacheLRURBIterator",
"LVCacheIteratorBase",
"LVImageCache",
"LVImageHeaderCache",
"create_cache_iterator",
]
+2 -1
View File
@@ -3,7 +3,7 @@ import gdb
from .core import DumpObj
from .display import DumpDisplayBuf
from .draw import InfoDrawUnit
from .misc import InfoStyle
from .misc import InfoStyle, DumpCache
from .debugger import Debugger
from .drivers import Lvglobal
@@ -22,6 +22,7 @@ Debugger()
# Dumps
DumpObj()
DumpDisplayBuf()
DumpCache()
# Infos
InfoStyle()
+2 -3
View File
@@ -1,5 +1,4 @@
from .lv_style import InfoStyle
from .lv_cache import DumpCache
__all__ = [
"InfoStyle",
]
__all__ = ["InfoStyle", "DumpCache"]
+40
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()
+30 -1
View File
@@ -1,7 +1,23 @@
from .core import LVObject, curr_inst, dump_obj_info
from .display import LVDisplay
from .draw import LVDrawBuf
from .misc import LVList, dump_style_info
from .misc import (
LVList,
dump_style_info,
LVRedBlackTree,
dump_rb_info,
LVCache,
dump_cache_info,
LVCacheEntry,
dump_cache_entry_info,
LVCacheLRURB,
dump_lru_rb_cache_info,
LVCacheLRURBIterator,
LVCacheIteratorBase,
LVImageCache,
LVImageHeaderCache,
create_cache_iterator,
)
__all__ = [
"LVObject",
@@ -11,4 +27,17 @@ __all__ = [
"LVList",
"dump_style_info",
"dump_obj_info",
"LVRedBlackTree",
"dump_rb_info",
"LVCache",
"dump_cache_info",
"LVCacheEntry",
"dump_cache_entry_info",
"LVCacheLRURB",
"dump_lru_rb_cache_info",
"LVCacheLRURBIterator",
"LVCacheIteratorBase",
"LVImageCache",
"LVImageHeaderCache",
"create_cache_iterator",
]
@@ -57,6 +57,16 @@ class LVGL:
yield unit
unit = unit.next
def image_cache(self):
from ..misc.lv_image_cache import LVImageCache
return LVImageCache(self.lv_global.img_cache)
def image_header_cache(self):
from ..misc.lv_image_header_cache import LVImageHeaderCache
return LVImageHeaderCache(self.lv_global.img_header_cache)
class _LVGLSingleton:
__slots__ = ("_lvgl", "_ready")
+9 -3
View File
@@ -109,13 +109,19 @@ class LVDrawBuf(Value):
if data_size <= 0:
raise ValueError(f"Invalid data size: {data_size}")
if data_size < expected_data_size:
raise ValueError(f"Data size mismatch: expected {expected_data_size}, got {data_size}")
raise ValueError(
f"Data size mismatch: expected {expected_data_size}, got {data_size}"
)
elif data_size > expected_data_size:
gdb.write(f"\033[93mData size mismatch: expected {expected_data_size}, got {data_size}\033[0m\n")
gdb.write(
f"\033[93mData size mismatch: expected {expected_data_size}, got {data_size}\033[0m\n"
)
# Read pixel data
pixel_data = (
gdb.selected_inferior().read_memory(int(data_ptr), expected_data_size).tobytes()
gdb.selected_inferior()
.read_memory(int(data_ptr), expected_data_size)
.tobytes()
)
if not pixel_data:
raise ValueError("Failed to read pixel data")
+194
View File
@@ -0,0 +1,194 @@
# LVGL GDB Scripts - Misc Module Extensions
This directory contains GDB debugging scripts for LVGL's miscellaneous data structures, including red-black trees and
cache systems.
## Objects
### 1. lv_rb.py - Red-Black Tree Support
Provides debugging support for LVGL's red-black tree (`lv_rb_t`) implementation.
**Classes:**
- `LVRedBlackTree` - Iterator for red-black tree nodes
**Functions:**
- `dump_rb_info(rb, datatype)` - Dump basic red-black tree information
**Usage:**
```gdbscript
# In GDB:
python tree = LVRedBlackTree(some_rb_tree)
python for data in tree: print(data)
python dump_rb_info(some_rb_tree, datatype)
```
### 2. lv_cache.py - Cache System Support
Provides debugging support for LVGL's cache system (`lv_cache_t`).
**Classes:**
- `LVCache` - Wrapper for cache objects
**Functions:**
- `dump_cache_info(cache, datatype)` - Dump cache information
**Usage:**
```gdbscript
# In GDB:
python cache = LVCache(some_cache)
python print(f"Cache usage: {cache.get_usage_percentage()}%")
python dump_cache_info(some_cache, datatype)
```
### 3. lv_cache_entry.py - Cache Entry Support
Provides debugging support for cache entries (`lv_cache_entry_t`).
**Classes:**
- `LVCacheEntry` - Wrapper for cache entries
**Functions:**
- `dump_cache_entry_info(entry, datatype)` - Dump cache entry information
**Usage:**
```gdbscript
# In GDB:
python entry = LVCacheEntry(some_entry)
python print(f"Ref count: {entry.get_ref_count()}")
python dump_cache_entry_info(some_entry, datatype)
```
### 4. lv_cache_lru_rb.py - LRU Cache with RB Tree Support
Specialized support for LRU-based caches using red-black trees.
**Classes:**
- `LVCacheLRURB` - Specialized cache wrapper for LRU RB implementations
**Functions:**
- `dump_lru_rb_cache_info(cache)` - Dump LRU RB cache information
### 5. lv_image_cache.py and lv_image_header_cache.py - Image Cache Support
Provides debugging support for LVGL's image cache (`lv_image_cache_t`).
**Classes:**
- `LVImageCache` - Wrapper for image cache objects
**Usage:**
```gdbscript
# In GDB:
python curr_inst().image_cache().print_info()
python curr_inst().image_cache().print_entries()
```
## Integration
All these modules are automatically imported and available when using the LVGL GDB scripts. You can access them through
the main `lvgl` module:
```gdbscript
# In GDB:
python from lvglgdb import LVRedBlackTree, LVCache, dump_rb_info, dump_cache_info
```
## Type Conversion Support
All wrapper classes support multiple input types:
- `gdb.Value` objects (from GDB expressions)
- `Value` objects (from lvglgdb.value module)
- Raw pointers as integers (e.g., `0x12345678`)
This allows flexible usage patterns:
```gdbscript
# From GDB variable
entry = LVCacheEntry(my_entry)
# From raw memory address
entry = LVCacheEntry(0x7fff12345678)
# From GDB expression result
entry = LVCacheEntry(gdb.parse_and_eval("*(lv_cache_entry_t*)0x7fff12345678"))
```
## Examples
### Debugging a Red-Black Tree
```gdbscript
# Create tree iterator from variable
tree = LVRedBlackTree(some_rb_variable)
# Create from raw pointer (0x12345678)
tree = LVRedBlackTree(0x12345678)
# Iterate through all nodes
for data_ptr in tree:
print(f"Data pointer: {data_ptr}")
# Get tree info
print(f"Tree has {len(tree)} nodes")
tree.print_info()
```
### Debugging a Cache
```gdbscript
# Create cache wrapper from variable
cache = LVCache(some_cache_variable)
# Create from raw pointer (0x12345678)
cache = LVCache(0x12345678)
# Check cache status
print(f"Cache usage: {cache.get_usage_percentage():.1f}%")
print(f"Cache enabled: {cache.is_enabled()}")
# Dump full info
cache.print_info()
```
### Debugging Cache Entries
```gdbscript
# Create entry wrapper from variable
entry = LVCacheEntry(some_entry_variable)
# Create from raw pointer (0x12345678)
entry = LVCacheEntry(0x12345678)
# Check entry status
print(f"Ref count: {entry.get_ref_count()}")
print(f"Invalid: {entry.is_invalid()}")
# Dump full info
entry.print_info()
```
## File Structure
- `lv_rb.py` - Red-black tree debugging support
- `lv_cache.py` - General cache debugging support
- `lv_cache_entry.py` - Cache entry debugging support
- `lv_cache_lru_rb.py` - LRU RB cache specialized support
- `lv_image_cache.py` - Image cache debugging support
- `lv_image_header_cache.py` - Image header cache debugging support
- `lv_cache_iter_base.py` - Cache iterator base class
- `lv_cache_iter_factory.py` - Cache iterator factory functions
- `__init__.py` - Module exports and initialization
+21
View File
@@ -1,7 +1,28 @@
from .lv_ll import LVList
from .lv_style import dump_style_info
from .lv_rb import LVRedBlackTree, dump_rb_info
from .lv_cache import LVCache, dump_cache_info
from .lv_cache_entry import LVCacheEntry, dump_cache_entry_info
from .lv_cache_lru_rb import LVCacheLRURB, dump_lru_rb_cache_info, LVCacheLRURBIterator
from .lv_cache_iter_base import LVCacheIteratorBase
from .lv_cache_iter_factory import create_cache_iterator
from .lv_image_cache import LVImageCache
from .lv_image_header_cache import LVImageHeaderCache
__all__ = [
"LVList",
"dump_style_info",
"LVRedBlackTree",
"dump_rb_info",
"LVCache",
"dump_cache_info",
"LVCacheEntry",
"dump_cache_entry_info",
"LVCacheLRURB",
"dump_lru_rb_cache_info",
"LVCacheIteratorBase",
"LVCacheLRURBIterator",
"LVImageCache",
"LVImageHeaderCache",
"create_cache_iterator",
]
+99
View File
@@ -0,0 +1,99 @@
from typing import Union, List, Optional, Dict
import gdb
from lvglgdb.value import Value
from .lv_cache_iter_factory import create_cache_iterator
class LVCache(Value):
"""LVGL cache wrapper - focuses on cache-level operations"""
def __init__(
self, cache: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str]
):
# Convert to Value first if needed
if isinstance(cache, int):
cache = Value(cache).cast("lv_cache_t", ptr=True)
if cache is None:
raise ValueError("Failed to cast pointer to lv_cache_t")
elif isinstance(cache, gdb.Value) and not isinstance(cache, Value):
cache = Value(cache)
elif not cache:
raise ValueError("Invalid cache")
self.datatype = (
gdb.lookup_type(datatype).pointer()
if isinstance(datatype, str)
else datatype
)
super().__init__(cache)
def print_info(self):
"""Dump cache information"""
print(f"Cache Info:")
print(f" Name: {self.name.as_string()}")
print(f" Node Size: {int(self.node_size)}")
print(f" Max Size: {int(self.max_size)}")
print(f" Current Size: {int(self.size)}")
print(f" Free Size: {int(self.max_size) - int(self.size)}")
print(f" Enabled: {bool(int(self.max_size) > 0)}")
# Try to identify cache type
try:
iterator = create_cache_iterator(self)
print(f" Iterator Type: {iterator.__class__.__name__}")
iterator.cache.print_info()
except gdb.error:
pass
def is_enabled(self):
"""Check if cache is enabled"""
return int(self.max_size) > 0
def get_usage_percentage(self):
"""Get cache usage percentage"""
if int(self.max_size) == 0:
return 0.0
return int(self.size) / int(self.max_size) * 100.0
def __iter__(self):
"""Create appropriate iterator based on cache class"""
return create_cache_iterator(self)
def items(self):
"""Get all cache entries as a list"""
entries = []
for entry in self:
entries.append(entry)
return entries
def print_entries(self, max_entries=10):
"""Print cache entries in readable format"""
cache_entries = self.items()
cache_entries_cnt = len(cache_entries)
print(f"Cache Entries ({cache_entries_cnt} total):")
count = 0
for i, entry in enumerate(cache_entries):
if count >= max_entries:
print(
f" ... showing first {max_entries} of {cache_entries_cnt} entries"
)
break
print(f" [{i}] {entry}")
count += 1
if count == 0:
print(" (empty)")
elif count < int(cache_entries_cnt):
print(f" ... {cache_entries_cnt - count} more entries not shown")
def dump_cache_info(
cache: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str]
):
"""Dump cache information"""
cache_obj = LVCache(cache, datatype)
cache_obj.print_info()
@@ -0,0 +1,103 @@
from typing import Union
import gdb
from lvglgdb.value import Value
class LVCacheEntry(Value):
"""LVGL cache entry wrapper - focuses on entry-level operations"""
def __init__(
self, entry: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str]
):
# Convert to Value first if needed
if isinstance(entry, int):
entry = Value(entry).cast("lv_cache_entry_t", ptr=True)
if entry is None:
raise ValueError("Failed to cast pointer to lv_cache_entry_t")
elif isinstance(entry, gdb.Value) and not isinstance(entry, Value):
entry = Value(entry)
elif not entry:
raise ValueError("Invalid cache entry")
self.datatype = (
gdb.lookup_type(datatype).pointer()
if isinstance(datatype, str)
else datatype
)
super().__init__(entry)
@classmethod
def from_data_ptr(
cls, data_ptr: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str]
):
"""Create LVCacheEntry from data pointer"""
if data_ptr.type == gdb.lookup_type("void").pointer() and datatype is None:
raise ValueError("Data pointer is void*, datatype must be provided")
data_ptr = data_ptr.cast(datatype)
entry_ptr = int(data_ptr) + data_ptr.type.target().sizeof
return cls(entry_ptr, datatype)
def print_info(self):
"""Dump cache entry information"""
print(f"Cache Entry Info:")
print(f" Reference Count: {int(self.ref_cnt)}")
print(f" Node Size: {int(self.node_size)}")
print(f" Flags: {int(self.flags)}")
print(f" Invalid: {self.is_invalid()}")
print(f" Disable Delete: {self.is_disabled_delete()}")
# Try to get cache info if available
try:
cache = self.cache
if cache:
print(f" Cache: {cache}")
except:
pass
def get_data(self):
"""Get entry data pointer"""
data_ptr = Value(int(self) - self.get_node_size()).cast(self.datatype)
return data_ptr
def is_invalid(self):
"""Check if entry is invalid"""
return bool(int(self.flags) & 1) # LV_CACHE_ENTRY_FLAG_INVALID
def is_disabled_delete(self):
"""Check if entry has disable delete flag"""
return bool(int(self.flags) & 2) # LV_CACHE_ENTRY_FLAG_DISABLE_DELETE
def get_ref_count(self):
"""Get reference count"""
return int(self.ref_cnt)
def get_node_size(self):
"""Get node size"""
return int(self.node_size)
def get_flags(self):
"""Get flags"""
return int(self.flags)
def __str__(self):
"""Provide better string representation for debugging"""
try:
data = self.get_data()
return f"CacheEntry(ref_cnt={self.get_ref_count()}, valid={not self.is_invalid()}, data={data.dereference()})"
except gdb.error:
pass
return super().__str__()
def dump_cache_entry_info(
entry: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str]
):
"""Dump cache entry information"""
entry_obj = LVCacheEntry(entry, datatype)
entry_obj.print_info()
@@ -0,0 +1,36 @@
from typing import List, Optional
import gdb
from lvglgdb.value import Value
class LVCacheIteratorBase:
"""Base class for cache iterators"""
def __init__(self, cache):
self.cache = cache
self._entries: List[Value] = []
self._current_index = 0
self._collect_entries()
def __iter__(self):
return self
def __next__(self):
"""Get next cache entry"""
if self._current_index < len(self._entries):
entry = self._entries[self._current_index]
self._current_index += 1
return entry
else:
raise StopIteration
def __len__(self):
return len(self._entries)
def __str__(self):
return f"{self.__class__.__name__}(cache={self.cache.name}, entries={len(self._entries)})"
def _collect_entries(self):
"""To be implemented by subclasses"""
raise NotImplementedError("Subclasses must implement _collect_entries")
@@ -0,0 +1,32 @@
import gdb
import re
from .lv_cache_iter_base import LVCacheIteratorBase
def create_cache_iterator(cache) -> LVCacheIteratorBase | None:
"""Factory function to create appropriate iterator for cache class"""
from .lv_cache_lru_rb import LVCacheLRURB
def get_class(s):
if s == "lv_cache_class_lru_rb_size":
return LVCacheLRURB
elif s == "lv_cache_class_lru_rb_count":
return LVCacheLRURB
return None
try:
if cache.clz:
symbol = cache.clz.format_string(symbols=True, address=False)
m = re.match("<(.*)>", symbol)
if m:
symbol = m.group(1)
cache_class = get_class(symbol)
if cache_class:
return iter(cache_class(cache))
except Exception:
raise TypeError(f"Unsupported cache type {cache}")
@@ -0,0 +1,104 @@
from typing import Union, List, Optional
import gdb
from lvglgdb.value import Value
from .lv_cache_iter_base import LVCacheIteratorBase
from .lv_rb import LVRedBlackTree
from .lv_cache_entry import LVCacheEntry
from .lv_cache import LVCache
class LVCacheLRURBIterator(LVCacheIteratorBase):
"""Iterator for LRU RB cache implementation - traverses linked list and red-black tree"""
def __init__(self, cache):
super().__init__(cache)
def _collect_entries(self):
"""Collect entries from LRU RB cache by traversing the linked list"""
try:
# Cast cache to lv_lru_rb_t_ to access internal structures
lru_cache = self.cache.cast("lv_lru_rb_t_", ptr=True)
if not lru_cache:
return
# Access the linked list
rb = lru_cache.rb
if not rb or not rb.root:
return
rb = LVRedBlackTree(rb)
for node in rb:
self._entries.append(
LVCacheEntry.from_data_ptr(node, self.cache.datatype)
)
except Exception as e:
print(f"Error in _collect_lru_entries: {e}")
import traceback
traceback.print_exc()
class LVCacheLRURB(LVCache):
"""LVGL LRU-based cache using red-black tree iterator"""
def __init__(self, cache: Union[Value, gdb.Value, int]):
# Convert to Value first if needed
if isinstance(cache, int):
cache = Value(cache).cast("lv_cache_lru_rb_t", ptr=True)
if cache is None:
raise ValueError("Failed to cast pointer to lv_cache_lru_rb_t")
elif isinstance(cache, gdb.Value) and not isinstance(cache, Value):
cache = Value(cache)
elif not cache:
raise ValueError("Invalid cache")
self.cache_base = cache
super().__init__(cache, cache.datatype)
def print_info(self):
"""Dump LRU RB cache information"""
print(f"LRU RB Cache Info:")
# Try to get cache class info
try:
clz = self.clz
if clz:
print(f" Cache Class: {clz}")
# Check if it's LRU RB based
if "lru_rb" in str(clz).lower():
print(f" Type: LRU with Red-Black Tree")
except:
pass
def is_count_based(self):
"""Check if this is count-based LRU cache"""
try:
name = str(self.name)
return "count" in name.lower() or "lru_rb_count" in str(self.clz).lower()
except:
return False
def is_size_based(self):
"""Check if this is size-based LRU cache"""
try:
name = str(self.name)
return "size" in name.lower() or "lru_rb_size" in str(self.clz).lower()
except:
return False
def __iter__(self):
"""Create iterator for this LRU RB cache"""
return LVCacheLRURBIterator(self)
def items(self):
"""Get all cache entries as a list"""
entries = []
for entry in self:
entries.append(entry)
return entries
def dump_lru_rb_cache_info(cache: Union[Value, gdb.Value, int]):
"""Dump LRU RB cache information"""
cache_obj = LVCacheLRURB(cache)
cache_obj.print_info()
@@ -0,0 +1,88 @@
from typing import Union
import gdb
from prettytable import PrettyTable
from lvglgdb.value import Value
from .lv_cache import LVCache
from .lv_cache_entry import LVCacheEntry
class LVImageCache(object):
def __init__(self, cache: Value):
self._cache = LVCache(cache, "lv_image_cache_data_t")
def print_info(self):
self._cache.print_info()
def print_entries(self):
"""Print image cache entries using prettytable format"""
table = PrettyTable()
table.field_names = [
"size",
"data_size",
"cf",
"rc",
"type",
"decoder",
"decoded",
"src",
]
table.align = "r" # Right align all columns by default
table.align["src"] = "l" # Left align source column
table.align["type"] = "c" # Center align type column
for entry in self._cache:
entry: LVCacheEntry
data_ptr = entry.get_data()
if not data_ptr:
continue
decoded = data_ptr.decoded
try:
header = decoded.header
w = int(header.w)
h = int(header.h)
cf = int(header.cf)
data_size = int(decoded.data_size) if decoded else 0
decoded_ptr = decoded.data if decoded else 0
decoder_name = data_ptr.decoder.name.as_string()
src_type = int(data_ptr.src_type)
src = data_ptr.src
ref_cnt = entry.get_ref_count()
size_str = f"{w}x{h}"
if src_type == 0: # LV_IMAGE_SRC_VARIABLE
src_str = src.format_string(
symbols=True, address=True, styling=True
)
type_str = "var"
elif src_type == 1: # LV_IMAGE_SRC_FILE
src_str = (
src.cast("char", ptr=True).as_string() if src else "(null)"
)
type_str = "file"
else: # Unknown type
src_str = f"{int(src):#x}" if src else "0x0"
type_str = "unkn"
table.add_row(
[
size_str,
f"{data_size}",
f"{cf}",
f"{ref_cnt}",
type_str,
decoder_name,
f"{int(decoded_ptr):#x}",
src_str,
]
)
except gdb.error as e:
table.add_row(["ERROR", "", "", "", "", "", "", str(e)])
continue
print(table)
@@ -0,0 +1,74 @@
from typing import Union
import gdb
from prettytable import PrettyTable
from lvglgdb.value import Value
from .lv_cache import LVCache
from .lv_cache_entry import LVCacheEntry
class LVImageHeaderCache(object):
def __init__(self, cache: Value):
self._cache = LVCache(cache, "lv_image_header_cache_data_t")
def print_info(self):
self._cache.print_info()
def print_entries(self):
"""Print image cache entries using prettytable format"""
table = PrettyTable()
table.field_names = ["size", "cf", "rc", "type", "decoder", "src"]
table.align = "r" # Right align all columns by default
table.align["src"] = "l" # Left align source column
table.align["type"] = "c" # Center align type column
for entry in self._cache:
entry: LVCacheEntry
data_ptr = entry.get_data()
if not data_ptr:
continue
try:
header = data_ptr.header
w = int(header.w)
h = int(header.h)
cf = int(header.cf)
src_type = int(data_ptr.src_type)
src = data_ptr.src
decoder_name = data_ptr.decoder.name.as_string()
ref_cnt = entry.get_ref_count()
size_str = f"{w}x{h}"
if src_type == 0: # LV_IMAGE_SRC_VARIABLE
src_str = src.format_string(
symbols=True, address=True, styling=True
)
type_str = "var"
elif src_type == 1: # LV_IMAGE_SRC_FILE
src_str = (
src.cast("char", ptr=True).as_string() if src else "(null)"
)
type_str = "file"
else: # Unknown type
src_str = f"{int(src):#x}" if src else "0x0"
type_str = "unkn"
table.add_row(
[
size_str,
f"{cf}",
f"{ref_cnt}",
type_str,
decoder_name,
src_str,
]
)
except gdb.error as e:
table.add_row(["ERROR", "", "", "", "", str(e)])
continue
print(table)
+183
View File
@@ -0,0 +1,183 @@
from typing import Union
import gdb
from lvglgdb.value import Value
class LVRedBlackTree(Value):
"""LVGL red-black tree iterator"""
def __init__(
self, rb: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str] = None
):
# Convert to Value first if needed
if isinstance(rb, int):
rb = Value(rb).cast("lv_rb_t", ptr=True)
if rb is None:
raise ValueError("Failed to cast pointer to lv_rb_t")
elif isinstance(rb, gdb.Value) and not isinstance(rb, Value):
rb = Value(rb)
elif not rb:
raise ValueError("Invalid red-black tree")
super().__init__(rb)
self.lv_rb_node_t = gdb.lookup_type("lv_rb_node_t").pointer()
self.datatype = (
gdb.lookup_type(datatype).pointer()
if isinstance(datatype, str)
else datatype
)
def minimum_from(self, node):
"""Find minimum node from given node"""
if not node:
return None
current = node
while current.left:
current = current.left
return current
def maximum_from(self, node):
"""Find maximum node from given node"""
if not node:
return None
current = node
while current.right:
current = current.right
return current
def minimum(self):
"""Find minimum node in the tree"""
root = self.root
return self.minimum_from(root)
def maximum(self):
"""Find maximum node in the tree"""
root = self.root
return self.maximum_from(root)
def __iter__(self):
"""Create a new iterator for this tree"""
return LVRedBlackTreeIterator(self)
def __len__(self):
"""Get the number of nodes in the tree"""
count = 0
for _ in self:
count += 1
return count
def get_data(self, node):
"""Get typed data from node, with optional type conversion"""
if not node or not node.data:
return None
data = node.data
if self.datatype:
return data.cast(self.datatype)
return data
def format_data(self, data):
"""Format data for display - simple GDB style"""
if data is None:
return "None"
try:
ptr_addr = f"0x{int(data):x}"
except:
return str(data)
if self.datatype and data:
try:
struct_data = data.dereference()
return f"{ptr_addr} -> {struct_data}"
except:
pass
return ptr_addr
def print_info(self):
"""Dump basic tree information"""
print(f"Red-Black Tree Info:")
print(f" Size: {int(self.size)}")
print(f" Node Count: {len(self)}")
print(f" Root: {self.root}")
if self.root:
root_color = "Red" if int(self.root.color) == 0 else "Black"
print(f" Root Color: {root_color}")
if self.datatype:
print(f" Data Type: {self.datatype}")
def print_tree(self, max_items=10):
"""Print tree data in a readable format"""
print(f"Red-Black Tree Contents ({len(self)} total items):")
count = 0
for i, data in enumerate(self):
if count >= max_items:
print(f" ... showing first {max_items} of {len(self)} items")
break
formatted = self.format_data(data)
print(f" [{i}] {formatted}")
count += 1
if count == 0:
print(" (empty)")
elif count < len(self):
print(f" ... {len(self) - count} more items not shown")
class LVRedBlackTreeIterator:
"""Iterator for LVRedBlackTree that supports multiple traversals"""
def __init__(self, tree: LVRedBlackTree):
self.tree = tree
self.current = tree.minimum()
def __iter__(self):
return self
def __next__(self):
if not self.current:
raise StopIteration
data = self.tree.get_data(self.current)
# Move to next node (in-order traversal)
if self.current.right:
self.current = self.tree.minimum_from(self.current.right)
else:
parent = self.current.parent
while parent and self.current == parent.right:
self.current = parent
parent = parent.parent
self.current = parent
return data
def __str__(self):
"""Better string representation for iterator"""
current = self.current
if not current:
return "LVRedBlackTreeIterator(ended)"
try:
data = self.tree.get_data(current)
if data:
return f"LVRedBlackTreeIterator(current={data})"
except:
pass
return f"LVRedBlackTreeIterator(current=0x{int(current):x})"
def dump_rb_info(
rb: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str] = None
):
"""Dump red-black tree information"""
tree = LVRedBlackTree(rb, datatype=datatype)
tree.print_info()
+27
View File
@@ -34,3 +34,30 @@ class Value(gdb.Value):
def super_value(self, attr: str) -> "Value":
return self[attr]
def as_string(self):
"""Convert to string if possible"""
try:
return self.string()
except gdb.error:
return str(self)
def __str__(self):
"""Provide better string representation for debugging"""
try:
ptr_val = int(self)
return f"({self.type})0x{ptr_val:x}"
except gdb.error:
pass
return super().__str__()
def __repr__(self):
"""Provide detailed representation"""
try:
content = self.dereference()
return f"Value({self.__str__()}: {content})"
except gdb.error:
pass
return f"Value({self.__str__()})"
+3
View File
@@ -0,0 +1,3 @@
numpy~=2.2.6
Pillow~=11.3.0
prettytable~=3.16.0