toolbox 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/fiber-debugging.md +1 -1
- data/context/getting-started.md +36 -14
- data/context/index.yaml +1 -1
- data/context/object-inspection.md +25 -25
- data/context/stack-inspection.md +3 -3
- data/data/toolbox/command.py +225 -0
- data/data/toolbox/context.py +358 -282
- data/data/toolbox/debugger/__init__.py +2 -0
- data/data/toolbox/debugger/gdb_backend.py +70 -1
- data/data/toolbox/debugger/lldb_backend.py +110 -9
- data/data/toolbox/fiber.py +837 -845
- data/data/toolbox/format.py +70 -65
- data/data/toolbox/heap.py +66 -56
- data/data/toolbox/init.py +9 -5
- data/data/toolbox/print.py +79 -0
- data/data/toolbox/rarray.py +4 -12
- data/data/toolbox/rbasic.py +31 -35
- data/data/toolbox/rbignum.py +3 -7
- data/data/toolbox/rclass.py +5 -5
- data/data/toolbox/readme.md +8 -8
- data/data/toolbox/rexception.py +3 -3
- data/data/toolbox/rfloat.py +8 -18
- data/data/toolbox/rhash.py +4 -12
- data/data/toolbox/rstring.py +4 -8
- data/data/toolbox/rstruct.py +6 -14
- data/data/toolbox/rsymbol.py +9 -33
- data/data/toolbox/{value.py → rvalue.py} +9 -9
- data/data/toolbox/stack.py +595 -605
- data/lib/toolbox/version.rb +1 -1
- data/readme.md +1 -1
- data.tar.gz.sig +0 -0
- metadata +3 -3
- metadata.gz.sig +0 -0
- data/data/toolbox/object.py +0 -84
|
@@ -70,6 +70,7 @@ read_cstring = _backend.read_cstring
|
|
|
70
70
|
create_value = _backend.create_value
|
|
71
71
|
create_value_from_int = _backend.create_value_from_int
|
|
72
72
|
create_value_from_address = _backend.create_value_from_address
|
|
73
|
+
register = _backend.register
|
|
73
74
|
|
|
74
75
|
# Constants
|
|
75
76
|
COMMAND_DATA = _backend.COMMAND_DATA
|
|
@@ -94,6 +95,7 @@ __all__ = [
|
|
|
94
95
|
'create_value',
|
|
95
96
|
'create_value_from_int',
|
|
96
97
|
'create_value_from_address',
|
|
98
|
+
'register',
|
|
97
99
|
'COMMAND_DATA',
|
|
98
100
|
'COMMAND_USER',
|
|
99
101
|
]
|
|
@@ -3,6 +3,7 @@ GDB backend implementation for unified debugger interface.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import gdb
|
|
6
|
+
import format
|
|
6
7
|
|
|
7
8
|
# Command categories
|
|
8
9
|
COMMAND_DATA = gdb.COMMAND_DATA
|
|
@@ -311,7 +312,7 @@ class Command:
|
|
|
311
312
|
"""Initialize and register a command.
|
|
312
313
|
|
|
313
314
|
Args:
|
|
314
|
-
name: Command name (e.g., "rb-
|
|
315
|
+
name: Command name (e.g., "rb-print")
|
|
315
316
|
category: Command category (COMMAND_DATA or COMMAND_USER)
|
|
316
317
|
"""
|
|
317
318
|
self.name = name
|
|
@@ -589,6 +590,74 @@ def create_value_from_address(address, value_type):
|
|
|
589
590
|
return Value(addr_val.dereference())
|
|
590
591
|
|
|
591
592
|
|
|
593
|
+
def register(name, handler_class, usage=None, category=COMMAND_USER):
|
|
594
|
+
"""Register a command with GDB using a handler class.
|
|
595
|
+
|
|
596
|
+
This creates a wrapper Command that handles parsing, terminal setup,
|
|
597
|
+
and delegates to the handler class for actual command logic.
|
|
598
|
+
|
|
599
|
+
Args:
|
|
600
|
+
name: Command name (e.g., "rb-print")
|
|
601
|
+
handler_class: Class to instantiate for handling the command
|
|
602
|
+
usage: Optional command.Usage specification for validation/help
|
|
603
|
+
category: Command category (COMMAND_USER, etc.)
|
|
604
|
+
|
|
605
|
+
Example:
|
|
606
|
+
class PrintHandler:
|
|
607
|
+
def invoke(self, arguments, terminal):
|
|
608
|
+
depth = arguments.get_option('depth', 1)
|
|
609
|
+
print(f"Depth: {depth}")
|
|
610
|
+
|
|
611
|
+
usage = command.Usage(
|
|
612
|
+
summary="Print something",
|
|
613
|
+
options={'depth': (int, 1)},
|
|
614
|
+
flags=['debug']
|
|
615
|
+
)
|
|
616
|
+
debugger.register("my-print", PrintHandler, usage=usage)
|
|
617
|
+
|
|
618
|
+
Returns:
|
|
619
|
+
The registered Command instance
|
|
620
|
+
"""
|
|
621
|
+
class RegisteredCommand(Command):
|
|
622
|
+
def __init__(self):
|
|
623
|
+
super(RegisteredCommand, self).__init__(name, category)
|
|
624
|
+
self.usage_spec = usage
|
|
625
|
+
self.handler_class = handler_class
|
|
626
|
+
|
|
627
|
+
def invoke(self, arg, from_tty):
|
|
628
|
+
"""GDB entry point - parses arguments and delegates to handler."""
|
|
629
|
+
# Create terminal first (needed for help text)
|
|
630
|
+
import format
|
|
631
|
+
terminal = format.create_terminal(from_tty)
|
|
632
|
+
|
|
633
|
+
try:
|
|
634
|
+
# Parse and validate arguments
|
|
635
|
+
if self.usage_spec:
|
|
636
|
+
arguments = self.usage_spec.parse(arg if arg else "")
|
|
637
|
+
else:
|
|
638
|
+
# Fallback to basic parsing without validation
|
|
639
|
+
import command
|
|
640
|
+
arguments = command.parse_arguments(arg if arg else "")
|
|
641
|
+
|
|
642
|
+
# Instantiate handler and invoke
|
|
643
|
+
handler = self.handler_class()
|
|
644
|
+
handler.invoke(arguments, terminal)
|
|
645
|
+
|
|
646
|
+
except ValueError as e:
|
|
647
|
+
# Validation error - show colored help
|
|
648
|
+
print(f"Error: {e}")
|
|
649
|
+
if self.usage_spec:
|
|
650
|
+
print()
|
|
651
|
+
self.usage_spec.print_to(terminal, name)
|
|
652
|
+
except Exception as e:
|
|
653
|
+
print(f"Error: {e}")
|
|
654
|
+
import traceback
|
|
655
|
+
traceback.print_exc()
|
|
656
|
+
|
|
657
|
+
# Instantiate and register the command with GDB
|
|
658
|
+
return RegisteredCommand()
|
|
659
|
+
|
|
660
|
+
|
|
592
661
|
|
|
593
662
|
|
|
594
663
|
|
|
@@ -6,6 +6,7 @@ would require more extensive testing and edge case handling.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import lldb
|
|
9
|
+
import format
|
|
9
10
|
|
|
10
11
|
# Command categories (LLDB doesn't have exact equivalents, using symbolic constants)
|
|
11
12
|
COMMAND_DATA = 0
|
|
@@ -427,7 +428,7 @@ class Command:
|
|
|
427
428
|
"""Initialize and register a command.
|
|
428
429
|
|
|
429
430
|
Args:
|
|
430
|
-
name: Command name (e.g., "rb-
|
|
431
|
+
name: Command name (e.g., "rb-print")
|
|
431
432
|
category: Command category (not used in LLDB)
|
|
432
433
|
"""
|
|
433
434
|
self.name = name
|
|
@@ -515,10 +516,13 @@ def lookup_type(type_name):
|
|
|
515
516
|
|
|
516
517
|
Returns:
|
|
517
518
|
Type object
|
|
519
|
+
|
|
520
|
+
Raises:
|
|
521
|
+
Error: If type cannot be found in debug symbols
|
|
518
522
|
"""
|
|
519
523
|
target = lldb.debugger.GetSelectedTarget()
|
|
520
524
|
|
|
521
|
-
# LLDB's FindFirstType searches
|
|
525
|
+
# LLDB's FindFirstType searches loaded debug symbols
|
|
522
526
|
lldb_type = target.FindFirstType(type_name)
|
|
523
527
|
|
|
524
528
|
if not lldb_type.IsValid():
|
|
@@ -534,8 +538,8 @@ def set_convenience_variable(name, value):
|
|
|
534
538
|
name: Variable name (without $ prefix)
|
|
535
539
|
value: Value to set (can be Value wrapper or native value)
|
|
536
540
|
|
|
537
|
-
Note: LLDB
|
|
538
|
-
|
|
541
|
+
Note: LLDB convenience variables are created using expression evaluation.
|
|
542
|
+
The variable name will be prefixed with $ automatically.
|
|
539
543
|
"""
|
|
540
544
|
if isinstance(value, Value):
|
|
541
545
|
native_value = value._value
|
|
@@ -545,16 +549,45 @@ def set_convenience_variable(name, value):
|
|
|
545
549
|
# LLDB approach: use expression to create a persistent variable
|
|
546
550
|
# Variables in LLDB are prefixed with $
|
|
547
551
|
target = lldb.debugger.GetSelectedTarget()
|
|
548
|
-
|
|
552
|
+
if not target:
|
|
553
|
+
return
|
|
554
|
+
|
|
555
|
+
process = target.GetProcess()
|
|
556
|
+
if not process:
|
|
557
|
+
return
|
|
558
|
+
|
|
559
|
+
thread = process.GetSelectedThread()
|
|
560
|
+
if not thread:
|
|
561
|
+
return
|
|
562
|
+
|
|
563
|
+
frame = thread.GetSelectedFrame()
|
|
564
|
+
if not frame:
|
|
565
|
+
return
|
|
549
566
|
|
|
550
|
-
# Create a persistent variable by evaluating an expression
|
|
567
|
+
# Create a persistent variable by evaluating an assignment expression
|
|
551
568
|
if hasattr(native_value, 'GetValue'):
|
|
552
569
|
# It's an SBValue
|
|
553
570
|
addr = native_value.GetValueAsUnsigned()
|
|
554
571
|
type_name = native_value.GetType().GetName()
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
572
|
+
|
|
573
|
+
# Use expr command to create persistent variable
|
|
574
|
+
# Format: expr <type> $varname = (<type>)address
|
|
575
|
+
expr = f"{type_name} ${name} = ({type_name})0x{addr:x}"
|
|
576
|
+
|
|
577
|
+
options = lldb.SBExpressionOptions()
|
|
578
|
+
options.SetIgnoreBreakpoints(True)
|
|
579
|
+
options.SetFetchDynamicValue(lldb.eNoDynamicValues)
|
|
580
|
+
options.SetTimeoutInMicroSeconds(100000) # 0.1 second timeout
|
|
581
|
+
options.SetTryAllThreads(False)
|
|
582
|
+
|
|
583
|
+
# Evaluate the expression to create the persistent variable
|
|
584
|
+
result = frame.EvaluateExpression(expr, options)
|
|
585
|
+
|
|
586
|
+
# Check for errors
|
|
587
|
+
if result.GetError().Fail():
|
|
588
|
+
error_msg = result.GetError().GetCString()
|
|
589
|
+
# Silently ignore errors for now - convenience variables are optional
|
|
590
|
+
pass
|
|
558
591
|
|
|
559
592
|
|
|
560
593
|
def execute(command, from_tty=False, to_string=False):
|
|
@@ -883,3 +916,71 @@ def create_value_from_int(int_value, value_type):
|
|
|
883
916
|
return Value(result)
|
|
884
917
|
|
|
885
918
|
|
|
919
|
+
def register(name, handler_class, usage=None, category=COMMAND_USER):
|
|
920
|
+
"""Register a command with LLDB using a handler class.
|
|
921
|
+
|
|
922
|
+
This creates a wrapper Command that handles parsing, terminal setup,
|
|
923
|
+
and delegates to the handler class for actual command logic.
|
|
924
|
+
|
|
925
|
+
Args:
|
|
926
|
+
name: Command name (e.g., "rb-print")
|
|
927
|
+
handler_class: Class to instantiate for handling the command
|
|
928
|
+
usage: Optional command.Usage specification for validation/help
|
|
929
|
+
category: Command category (COMMAND_USER, etc.)
|
|
930
|
+
|
|
931
|
+
Example:
|
|
932
|
+
class PrintHandler:
|
|
933
|
+
def invoke(self, arguments, terminal):
|
|
934
|
+
depth = arguments.get_option('depth', 1)
|
|
935
|
+
print(f"Depth: {depth}")
|
|
936
|
+
|
|
937
|
+
usage = command.Usage(
|
|
938
|
+
summary="Print something",
|
|
939
|
+
options={'depth': (int, 1)},
|
|
940
|
+
flags=['debug']
|
|
941
|
+
)
|
|
942
|
+
debugger.register("my-print", PrintHandler, usage=usage)
|
|
943
|
+
|
|
944
|
+
Returns:
|
|
945
|
+
The registered Command instance
|
|
946
|
+
"""
|
|
947
|
+
class RegisteredCommand(Command):
|
|
948
|
+
def __init__(self):
|
|
949
|
+
super(RegisteredCommand, self).__init__(name, category)
|
|
950
|
+
self.usage_spec = usage
|
|
951
|
+
self.handler_class = handler_class
|
|
952
|
+
|
|
953
|
+
def invoke(self, arg, from_tty):
|
|
954
|
+
"""LLDB entry point - parses arguments and delegates to handler."""
|
|
955
|
+
# Create terminal first (needed for help text)
|
|
956
|
+
import format
|
|
957
|
+
terminal = format.create_terminal(from_tty)
|
|
958
|
+
|
|
959
|
+
try:
|
|
960
|
+
# Parse and validate arguments
|
|
961
|
+
if self.usage_spec:
|
|
962
|
+
arguments = self.usage_spec.parse(arg if arg else "")
|
|
963
|
+
else:
|
|
964
|
+
# Fallback to basic parsing without validation
|
|
965
|
+
import command
|
|
966
|
+
arguments = command.parse_arguments(arg if arg else "")
|
|
967
|
+
|
|
968
|
+
# Instantiate handler and invoke
|
|
969
|
+
handler = self.handler_class()
|
|
970
|
+
handler.invoke(arguments, terminal)
|
|
971
|
+
|
|
972
|
+
except ValueError as e:
|
|
973
|
+
# Validation error - show colored help
|
|
974
|
+
print(f"Error: {e}")
|
|
975
|
+
if self.usage_spec:
|
|
976
|
+
print()
|
|
977
|
+
self.usage_spec.print_to(terminal, name)
|
|
978
|
+
except Exception as e:
|
|
979
|
+
print(f"Error: {e}")
|
|
980
|
+
import traceback
|
|
981
|
+
traceback.print_exc()
|
|
982
|
+
|
|
983
|
+
# Instantiate and register the command with LLDB
|
|
984
|
+
return RegisteredCommand()
|
|
985
|
+
|
|
986
|
+
|