Skip to content

Commit 3c511ca

Browse files
committed
Prefer return None rather than exception.
1 parent d3563ac commit 3c511ca

19 files changed

+441
-139
lines changed

data/toolbox/debugger/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
read_cstring = _backend.read_cstring
6969
create_value = _backend.create_value
7070
create_value_from_int = _backend.create_value_from_int
71+
create_value_from_address = _backend.create_value_from_address
7172

7273
# Constants
7374
COMMAND_DATA = _backend.COMMAND_DATA
@@ -90,6 +91,7 @@
9091
'read_cstring',
9192
'create_value',
9293
'create_value_from_int',
94+
'create_value_from_address',
9395
'COMMAND_DATA',
9496
'COMMAND_USER',
9597
]

data/toolbox/debugger/gdb_backend.py

Lines changed: 115 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,20 +97,73 @@ def __getitem__(self, key):
9797
key: Field name (str) or array index (int)
9898
9999
Returns:
100-
Value of the field/element
100+
Value of the field/element, or None if field doesn't exist or is invalid
101101
"""
102-
return Value(self._value[key])
102+
try:
103+
result = self._value[key]
104+
return Value(result)
105+
except (gdb.error, KeyError, AttributeError):
106+
return None
107+
108+
def __add__(self, other):
109+
"""Add to this value.
110+
111+
Args:
112+
other: Value, gdb.Value, or integer to add
113+
114+
Returns:
115+
Integer result of addition
116+
"""
117+
if isinstance(other, Value):
118+
return int(self._value) + int(other._value)
119+
elif isinstance(other, gdb.Value):
120+
return int(self._value) + int(other)
121+
else:
122+
return int(self._value) + int(other)
123+
124+
def __radd__(self, other):
125+
"""Reverse add - when Value is on the right side of +.
126+
127+
Args:
128+
other: Value, gdb.Value, or integer to add
129+
130+
Returns:
131+
Integer result of addition
132+
"""
133+
return self.__add__(other)
103134

104-
def __add__(self, offset):
105-
"""Pointer arithmetic: add offset.
135+
def __sub__(self, other):
136+
"""Subtract from this value.
106137
107138
Args:
108-
offset: Integer offset to add
139+
other: Value, gdb.Value, or integer to subtract
109140
110141
Returns:
111-
New Value with adjusted pointer
142+
Integer result of subtraction
112143
"""
113-
return Value(self._value + offset)
144+
if isinstance(other, Value):
145+
return int(self._value) - int(other._value)
146+
elif isinstance(other, gdb.Value):
147+
return int(self._value) - int(other)
148+
else:
149+
return int(self._value) - int(other)
150+
151+
def __rsub__(self, other):
152+
"""Reverse subtract - when Value is on the right side of -.
153+
154+
Args:
155+
other: Value, gdb.Value, or integer to subtract from
156+
157+
Returns:
158+
Integer result of subtraction
159+
"""
160+
if isinstance(other, Value):
161+
return int(other._value) - int(self._value)
162+
elif isinstance(other, gdb.Value):
163+
return int(other) - int(self._value)
164+
else:
165+
return int(other) - int(self._value)
166+
114167

115168
@property
116169
def type(self):
@@ -154,6 +207,17 @@ def pointer(self):
154207
"""
155208
return Type(self._type.pointer())
156209

210+
def array(self, count):
211+
"""Get array type of this type.
212+
213+
Args:
214+
count: Number of elements in the array
215+
216+
Returns:
217+
Type representing array of this type
218+
"""
219+
return Type(self._type.array(count))
220+
157221
@property
158222
def native(self):
159223
"""Get the underlying native GDB type.
@@ -378,16 +442,21 @@ def create_value_from_int(int_value, value_type):
378442
This is used when the integer itself IS the value (like VALUE which is a pointer).
379443
380444
Args:
381-
int_value: Integer value (not an address to read from)
445+
int_value: Integer value, or Value object that will be converted to int
382446
value_type: Type object (or native gdb.Type) to cast to
383447
384448
Returns:
385449
Value object with the integer value
386450
387451
Examples:
388452
>>> value_type = debugger.lookup_type('VALUE')
389-
>>> obj = debugger.create_value_from_int(0x7fff12345678, value_type)
453+
>>> obj_address = page['start'] # Value object
454+
>>> obj = debugger.create_value_from_int(obj_address, value_type)
390455
"""
456+
# Convert to integer if needed (handles Value objects via __int__)
457+
if hasattr(int_value, '__int__'):
458+
int_value = int(int_value)
459+
391460
# Unwrap Type if needed
392461
if isinstance(value_type, Type):
393462
value_type = value_type._type
@@ -397,4 +466,41 @@ def create_value_from_int(int_value, value_type):
397466
return Value(int_val.cast(value_type))
398467

399468

469+
def create_value_from_address(address, value_type):
470+
"""Create a typed Value from a memory address.
471+
472+
In GDB, this is equivalent to casting the address to a pointer type
473+
and dereferencing it.
474+
475+
Args:
476+
address: Memory address (as integer, or Value object representing a pointer)
477+
value_type: Type object (or native gdb.Type) representing the type
478+
479+
Returns:
480+
Value object representing the data at that address
481+
482+
Examples:
483+
>>> rbasic_type = debugger.lookup_type('struct RBasic')
484+
>>> array_type = rbasic_type.array(100)
485+
>>> page_start = page['start'] # Value object
486+
>>> page_array = debugger.create_value_from_address(page_start, array_type)
487+
"""
488+
# Convert to integer if needed (handles Value objects via __int__)
489+
if hasattr(address, '__int__'):
490+
address = int(address)
491+
492+
# Unwrap Type if needed
493+
if isinstance(value_type, Type):
494+
value_type = value_type._type
495+
496+
# Create a pointer to the type and dereference it
497+
# This is GDB's way of saying "interpret this address as this type"
498+
ptr_type = value_type.pointer()
499+
addr_val = gdb.Value(address).cast(ptr_type)
500+
return Value(addr_val.dereference())
501+
502+
503+
504+
505+
400506

data/toolbox/debugger/lldb_backend.py

Lines changed: 146 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,65 @@ def __ge__(self, other):
125125
else:
126126
return self._value.GetValueAsUnsigned() >= int(other)
127127

128+
def __add__(self, other):
129+
"""Add to this value.
130+
131+
Args:
132+
other: Value, SBValue, or integer to add
133+
134+
Returns:
135+
Integer result of addition
136+
"""
137+
if isinstance(other, Value):
138+
return self._value.GetValueAsUnsigned() + other._value.GetValueAsUnsigned()
139+
elif isinstance(other, lldb.SBValue):
140+
return self._value.GetValueAsUnsigned() + other.GetValueAsUnsigned()
141+
else:
142+
return self._value.GetValueAsUnsigned() + int(other)
143+
144+
def __radd__(self, other):
145+
"""Reverse add - when Value is on the right side of +.
146+
147+
Args:
148+
other: Value, SBValue, or integer to add
149+
150+
Returns:
151+
Integer result of addition
152+
"""
153+
return self.__add__(other)
154+
155+
def __sub__(self, other):
156+
"""Subtract from this value.
157+
158+
Args:
159+
other: Value, SBValue, or integer to subtract
160+
161+
Returns:
162+
Integer result of subtraction
163+
"""
164+
if isinstance(other, Value):
165+
return self._value.GetValueAsUnsigned() - other._value.GetValueAsUnsigned()
166+
elif isinstance(other, lldb.SBValue):
167+
return self._value.GetValueAsUnsigned() - other.GetValueAsUnsigned()
168+
else:
169+
return self._value.GetValueAsUnsigned() - int(other)
170+
171+
def __rsub__(self, other):
172+
"""Reverse subtract - when Value is on the right side of -.
173+
174+
Args:
175+
other: Value, SBValue, or integer to subtract from
176+
177+
Returns:
178+
Integer result of subtraction
179+
"""
180+
if isinstance(other, Value):
181+
return other._value.GetValueAsUnsigned() - self._value.GetValueAsUnsigned()
182+
elif isinstance(other, lldb.SBValue):
183+
return other.GetValueAsUnsigned() - self._value.GetValueAsUnsigned()
184+
else:
185+
return int(other) - self._value.GetValueAsUnsigned()
186+
128187
def cast(self, type_obj):
129188
"""Cast this value to a different type.
130189
@@ -164,10 +223,15 @@ def __getitem__(self, key):
164223
key: Field name (str) or array index (int)
165224
166225
Returns:
167-
Value of the field/element
226+
Value of the field/element, or None if field doesn't exist or is invalid
168227
"""
169228
if isinstance(key, str):
170-
return Value(self._value.GetChildMemberWithName(key))
229+
result = self._value.GetChildMemberWithName(key)
230+
# Check if the result is valid
231+
if result.IsValid() and not result.GetError().Fail():
232+
return Value(result)
233+
else:
234+
return None
171235
else:
172236
# For integer index: check if this is a pointer type or array type
173237
# Both need pointer-style arithmetic (arrays for flexible array members)
@@ -317,6 +381,17 @@ def pointer(self):
317381
"""
318382
return Type(self._type.GetPointerType())
319383

384+
def array(self, count):
385+
"""Get array type of this type.
386+
387+
Args:
388+
count: Number of elements in the array (count - 1 for LLDB)
389+
390+
Returns:
391+
Type representing array of this type
392+
"""
393+
return Type(self._type.GetArrayType(count + 1))
394+
320395
@property
321396
def native(self):
322397
"""Get the underlying native LLDB type.
@@ -618,9 +693,20 @@ def create_value(address, value_type):
618693
if hasattr(address, '__int__'):
619694
address = int(address)
620695

696+
target = lldb.debugger.GetSelectedTarget()
697+
698+
# For array types, use expression evaluation (CreateValueFromData doesn't work for large arrays)
699+
if value_type.IsArrayType():
700+
type_name = value_type.GetName()
701+
expr = f"*({type_name}*)0x{address:x}"
702+
addr_value = target.CreateValueFromExpression(
703+
f"array_0x{address:x}",
704+
expr
705+
)
706+
return Value(addr_value)
707+
621708
# OPTIMIZATION: For simple types like VALUE (unsigned long), use CreateValueFromData
622709
# which avoids expression evaluation and is much faster for bulk operations
623-
target = lldb.debugger.GetSelectedTarget()
624710
process = target.GetProcess()
625711

626712
# For scalar types (integers/pointers), we can read and create directly
@@ -655,6 +741,56 @@ def create_value(address, value_type):
655741
return Value(addr_value)
656742

657743

744+
def create_value_from_address(address, value_type):
745+
"""Create a typed Value from a memory address.
746+
747+
This uses CreateValueFromAddress to create a value of the given type at the
748+
specified address. This is more efficient than CreateValueFromExpression
749+
and supports array types directly.
750+
751+
Args:
752+
address: Memory address (as integer, or Value object representing a pointer)
753+
value_type: Type object (or native lldb.SBType) representing the type
754+
755+
Returns:
756+
Value object representing the data at that address
757+
758+
Examples:
759+
>>> rbasic_type = debugger.lookup_type('struct RBasic')
760+
>>> array_type = rbasic_type.array(100)
761+
>>> page_start = page['start'] # Value object
762+
>>> page_array = debugger.create_value_from_address(page_start, array_type)
763+
"""
764+
# Convert to integer address if needed (handles Value objects via __int__)
765+
if hasattr(address, '__int__'):
766+
address = int(address)
767+
768+
# Unwrap Type if needed
769+
if isinstance(value_type, Type):
770+
value_type = value_type._type
771+
772+
target = lldb.debugger.GetSelectedTarget()
773+
774+
# CreateValueFromAddress takes an SBAddress, not an integer
775+
# We need to create an SBAddress from the load address
776+
sb_addr = target.ResolveLoadAddress(address)
777+
if not sb_addr.IsValid():
778+
raise MemoryError(f"Invalid address: 0x{address:x}")
779+
780+
# CreateValueFromAddress takes an address and creates a value of the given type
781+
# reading from that memory location
782+
addr_value = target.CreateValueFromAddress(
783+
f"val_at_0x{address:x}",
784+
sb_addr,
785+
value_type
786+
)
787+
788+
if not addr_value.IsValid():
789+
raise MemoryError(f"Failed to create value from address 0x{address:x}")
790+
791+
return Value(addr_value)
792+
793+
658794
def create_value_from_int(int_value, value_type):
659795
"""Create a typed Value from an integer (not a memory address to read from).
660796
@@ -663,16 +799,21 @@ def create_value_from_int(int_value, value_type):
663799
the integer itself.
664800
665801
Args:
666-
int_value: Integer value (not an address to read from)
802+
int_value: Integer value, or Value object that will be converted to int
667803
value_type: Type object (or native lldb.SBType) to cast to
668804
669805
Returns:
670806
Value object with the integer value
671807
672808
Examples:
673809
>>> value_type = debugger.lookup_type('VALUE')
674-
>>> obj = debugger.create_value_from_int(0x7fff12345678, value_type)
810+
>>> obj_address = page['start'] # Value object
811+
>>> obj = debugger.create_value_from_int(obj_address, value_type)
675812
"""
813+
# Convert to integer if needed (handles Value objects via __int__)
814+
if hasattr(int_value, '__int__'):
815+
int_value = int(int_value)
816+
676817
# Unwrap Type if needed
677818
if isinstance(value_type, Type):
678819
value_type = value_type._type

0 commit comments

Comments
 (0)