mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-10 12:47:51 +08:00
chore(gdb): support rb/cache associate debug obj and commands (#9089)
This commit is contained in:
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from .lv_style import InfoStyle
|
||||
from .lv_cache import DumpCache
|
||||
|
||||
__all__ = [
|
||||
"InfoStyle",
|
||||
]
|
||||
__all__ = ["InfoStyle", "DumpCache"]
|
||||
|
||||
@@ -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()
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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__()})"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
numpy~=2.2.6
|
||||
Pillow~=11.3.0
|
||||
prettytable~=3.16.0
|
||||
Reference in New Issue
Block a user