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,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

View File

@@ -0,0 +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",
]

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()

View File

@@ -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()

View File

@@ -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")

View File

@@ -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}")

View File

@@ -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()

View File

@@ -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)

View File

@@ -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)

View File

@@ -0,0 +1,53 @@
from typing import Union
import gdb
from lvglgdb.value import Value
class LVList(Value):
"""LVGL linked list iterator"""
def __init__(self, ll: Value, nodetype: Union[gdb.Type, str] = None):
if not ll:
raise ValueError("Invalid linked list")
super().__init__(ll)
self.nodetype = (
gdb.lookup_type(nodetype).pointer()
if isinstance(nodetype, str)
else nodetype
)
self.lv_ll_node_t = gdb.lookup_type("lv_ll_node_t").pointer()
self.current = self.head
self._next_offset = self.n_size + self.lv_ll_node_t.sizeof
self._prev_offset = self.n_size
def _next(self, node):
next_value = Value(int(node) + self._next_offset)
return next_value.cast(self.lv_ll_node_t, ptr=True).dereference()
def _prev(self, node):
prev_value = Value(int(node) + self._prev_offset)
return prev_value.cast(self.lv_ll_node_t, ptr=True).dereference()
def __iter__(self):
return self
def __next__(self):
if not self.current:
raise StopIteration
nodetype = self.nodetype if self.nodetype else self.lv_ll_node_t
node = self.current.cast(nodetype)
self.current = self._next(self.current)
return node
@property
def len(self):
len = 0
node = self.head
while node:
len += 1
node = self._next(node)
return len

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()

View File

@@ -0,0 +1,7 @@
from lvglgdb.value import Value
def dump_style_info(style: Value):
prop = int(style.prop)
value = style.value
print(f"{prop} = {value}")