Skip to content

Commit f055874

Browse files
Migrate all commands.
1 parent 53799b6 commit f055874

File tree

4 files changed

+125
-196
lines changed

4 files changed

+125
-196
lines changed

data/toolbox/context.py

Lines changed: 28 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -230,47 +230,23 @@ def print_info(self, terminal):
230230
pass
231231

232232

233-
class RubyContextCommand(debugger.Command):
234-
"""Show current execution context and set convenience variables.
233+
class RubyContextHandler:
234+
"""Show current execution context and set convenience variables."""
235235

236-
This command automatically discovers the current thread's execution context
237-
and displays detailed information about it, while also setting up convenience
238-
variables for easy inspection.
236+
USAGE = command.Usage(
237+
summary="Show current execution context and set convenience variables",
238+
parameters=[],
239+
options={},
240+
flags=[],
241+
examples=[
242+
("rb-context", "Display execution context info"),
243+
("rb-context; rb-print $errinfo", "Show context then print exception")
244+
]
245+
)
239246

240-
Usage:
241-
rb-context
242-
243-
Displays:
244-
- Execution context pointer and details
245-
- VM stack information
246-
- Control frame pointer
247-
- Exception information (if any)
248-
249-
Sets these convenience variables:
250-
$ec - Current execution context (rb_execution_context_t *)
251-
$cfp - Current control frame pointer
252-
$errinfo - Current exception (if any)
253-
254-
Example:
255-
(gdb) rb-context
256-
Execution Context:
257-
$ec = <rb_execution_context_t *@0x...>
258-
VM Stack: <VALUE *@0x...> size=1024
259-
$cfp = <rb_control_frame_t *@0x...>
260-
Exception: None
261-
262-
(gdb) rb-object-print $errinfo
263-
(gdb) rb-object-print $ec->cfp->sp[-1]
264-
"""
265-
266-
def __init__(self):
267-
super(RubyContextCommand, self).__init__("rb-context", debugger.COMMAND_USER)
268-
269-
def invoke(self, arg, from_tty):
247+
def invoke(self, arguments, terminal):
270248
"""Execute the rb-context command."""
271249
try:
272-
terminal = format.create_terminal(from_tty)
273-
274250
# Get current execution context
275251
ctx = RubyContext.current()
276252

@@ -314,30 +290,21 @@ def invoke(self, arg, from_tty):
314290
traceback.print_exc()
315291

316292

317-
class RubyContextStorageCommand(debugger.Command):
318-
"""Print the fiber storage from the current execution context.
293+
class RubyContextStorageHandler:
294+
"""Print the fiber storage from the current execution context."""
319295

320-
This command is a convenience wrapper around rb-object-print that
321-
specifically prints $ec->storage (the inheritable fiber storage).
296+
USAGE = command.Usage(
297+
summary="Print fiber storage from current execution context",
298+
parameters=[],
299+
options={'depth': (int, 1, 'Recursion depth for nested objects')},
300+
flags=[('debug', 'Show debug information')],
301+
examples=[
302+
("rb-context-storage", "Print storage with default depth"),
303+
("rb-context-storage --depth 3", "Print storage with depth 3")
304+
]
305+
)
322306

323-
Usage:
324-
rb-context-storage [--depth N] [--debug]
325-
326-
All flags are passed through to rb-object-print.
327-
328-
Example:
329-
(gdb) rb-context
330-
(gdb) rb-context-storage --depth 3
331-
332-
This is equivalent to:
333-
(gdb) rb-context
334-
(gdb) rb-object-print $ec->storage --depth 3
335-
"""
336-
337-
def __init__(self):
338-
super(RubyContextStorageCommand, self).__init__("rb-context-storage", debugger.COMMAND_USER)
339-
340-
def invoke(self, arg, from_tty):
307+
def invoke(self, arguments, terminal):
341308
"""Execute the rb-context-storage command."""
342309
try:
343310
# Get current execution context
@@ -396,6 +363,5 @@ def invoke(self, arg, from_tty):
396363

397364

398365
# Register commands
399-
RubyContextCommand()
400-
RubyContextStorageCommand()
401-
366+
debugger.register("rb-context", RubyContextHandler, usage=RubyContextHandler.USAGE)
367+
debugger.register("rb-context-storage", RubyContextStorageHandler, usage=RubyContextStorageHandler.USAGE)

data/toolbox/fiber.py

Lines changed: 61 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -220,22 +220,29 @@ def print_info(self, terminal):
220220
print(terminal.print_type_tag('rb_control_frame_t', int(self.cfp), None))
221221

222222

223-
class RubyFiberScanHeapCommand(debugger.Command):
223+
class RubyFiberScanHeapHandler:
224224
"""Scan heap and list all Ruby fibers."""
225225

226+
USAGE = command.Usage(
227+
summary="Scan heap and list all Ruby fibers",
228+
parameters=[],
229+
options={
230+
'limit': (int, None, 'Maximum fibers to find'),
231+
'cache': (str, None, 'Cache file to use (default: fibers.json)')
232+
},
233+
flags=[
234+
('terminated', 'Include terminated fibers in results')
235+
],
236+
examples=[
237+
("rb-fiber-scan-heap", "Find all non-terminated fibers"),
238+
("rb-fiber-scan-heap --limit 10", "Find first 10 fibers"),
239+
("rb-fiber-scan-heap --terminated", "Include terminated fibers"),
240+
("rb-fiber-scan-heap --cache my.json", "Use custom cache file")
241+
]
242+
)
243+
226244
def __init__(self):
227-
super(RubyFiberScanHeapCommand, self).__init__("rb-fiber-scan-heap", debugger.COMMAND_USER)
228245
self.heap = heap.RubyHeap()
229-
230-
def usage(self):
231-
"""Print usage information."""
232-
print("Usage: rb-fiber-scan-heap [--limit N] [--cache [filename]] [--terminated]")
233-
print("Examples:")
234-
print(" rb-fiber-scan-heap # Find all non-terminated fibers")
235-
print(" rb-fiber-scan-heap --limit 10 # Find first 10 non-terminated fibers")
236-
print(" rb-fiber-scan-heap --terminated # Include terminated fibers")
237-
print(" rb-fiber-scan-heap --cache # Use fibers.json cache")
238-
print(" rb-fiber-scan-heap --cache my.json # Use custom cache file")
239246

240247
def save_cache(self, fiber_values, filename):
241248
"""Save fiber VALUE addresses to cache file.
@@ -474,29 +481,21 @@ def _print_fiber_info(self, terminal, index, fiber_obj):
474481
print()
475482

476483

477-
class RubyFiberScanSwitchCommand(debugger.Command):
478-
"""Switch to a fiber from the scan heap cache.
484+
class RubyFiberScanSwitchHandler:
485+
"""Switch to a fiber from the scan heap cache."""
479486

480-
Usage: rb-fiber-scan-switch <index>
481-
Example: rb-fiber-scan-switch 0
482-
rb-fiber-scan-switch 2
483-
484-
Note: This command requires a fiber cache populated by 'rb-fiber-scan-heap'.
485-
"""
487+
USAGE = command.Usage(
488+
summary="Switch to a fiber from scan cache by index",
489+
parameters=[('index', 'Fiber index from rb-fiber-scan-heap')],
490+
options={},
491+
flags=[],
492+
examples=[
493+
("rb-fiber-scan-switch 0", "Switch to first fiber"),
494+
("rb-fiber-scan-switch 2", "Switch to third fiber")
495+
]
496+
)
486497

487-
def __init__(self):
488-
super(RubyFiberScanSwitchCommand, self).__init__("rb-fiber-scan-switch", debugger.COMMAND_USER)
489-
490-
def usage(self):
491-
"""Print usage information."""
492-
print("Usage: rb-fiber-scan-switch <index>")
493-
print("Examples:")
494-
print(" rb-fiber-scan-switch 0 # Switch to fiber #0")
495-
print(" rb-fiber-scan-switch 2 # Switch to fiber #2")
496-
print()
497-
print("Note: Run 'rb-fiber-scan-heap' first to populate the fiber cache.")
498-
499-
def invoke(self, arg, from_tty):
498+
def invoke(self, arguments, terminal):
500499
global _fiber_cache
501500

502501
if not arg or not arg.strip():
@@ -650,42 +649,25 @@ def deactivate(self):
650649
gdb.invalidate_cached_frames()
651650

652651

653-
class RubyFiberSwitchCommand(debugger.Command):
654-
"""Switch debugger's stack view to a specific fiber.
655-
656-
Usage: rb-fiber-switch <fiber_value_or_address>
657-
rb-fiber-switch off
658-
659-
Examples:
660-
rb-fiber-switch 0x7fffdc409ca8 # VALUE address
661-
rb-fiber-switch $fiber_val # Debugger variable
662-
rb-fiber-switch off # Deactivate unwinder (GDB only)
652+
class RubyFiberSwitchHandler:
653+
"""Switch debugger's stack view to a specific fiber."""
663654

664-
This uses a custom unwinder (GDB only) to make the debugger follow the fiber's saved
665-
stack, allowing you to use 'bt', 'up', 'down', 'frame', etc.
666-
Works even with core dumps!
667-
668-
Based on technique from Facebook Folly fibers.
669-
"""
655+
USAGE = command.Usage(
656+
summary="Switch debugger stack view to a specific fiber",
657+
parameters=[('fiber', 'Fiber VALUE/address or "off" to deactivate')],
658+
options={},
659+
flags=[],
660+
examples=[
661+
("rb-fiber-switch 0x7fffdc409ca8", "Switch to fiber at address"),
662+
("rb-fiber-switch $fiber", "Switch using debugger variable"),
663+
("rb-fiber-switch off", "Deactivate unwinder (GDB only)")
664+
]
665+
)
670666

671667
def __init__(self):
672-
super(RubyFiberSwitchCommand, self).__init__("rb-fiber-switch", debugger.COMMAND_USER)
673668
if debugger.DEBUGGER_NAME == 'gdb':
674669
self._ensure_unwinder()
675670

676-
def usage(self):
677-
"""Print usage information."""
678-
print("Usage: rb-fiber-switch <fiber_value_or_address>")
679-
print(" rb-fiber-switch off")
680-
print("Examples:")
681-
print(" rb-fiber-switch 0x7fffdc409ca8 # VALUE address")
682-
print(" rb-fiber-switch $fiber # Debugger variable")
683-
print(" rb-fiber-switch off # Deactivate unwinder (GDB only)")
684-
print()
685-
print("After switching, you can use: bt, up, down, frame, info locals, etc.")
686-
if debugger.DEBUGGER_NAME != 'gdb':
687-
print("Note: Stack unwinding is only supported in GDB")
688-
689671
def _ensure_unwinder(self):
690672
"""Ensure the fiber unwinder is registered (GDB only)."""
691673
global _fiber_unwinder
@@ -840,26 +822,20 @@ def invoke(self, arg, from_tty):
840822
print(" rb-fiber-switch off")
841823

842824

843-
class RubyFiberScanStackTraceAllCommand(debugger.Command):
844-
"""Print stack traces for all fibers in the scan cache.
845-
846-
Usage: rb-fiber-scan-stack-trace-all
825+
class RubyFiberScanStackTraceAllHandler:
826+
"""Print stack traces for all fibers in the scan cache."""
847827

848-
This command prints the Ruby stack trace for each fiber that was
849-
found by 'rb-fiber-scan-heap'. Run that command first to populate
850-
the fiber cache.
851-
"""
828+
USAGE = command.Usage(
829+
summary="Print stack traces for all cached fibers",
830+
parameters=[],
831+
options={},
832+
flags=[],
833+
examples=[
834+
("rb-fiber-scan-heap; rb-fiber-scan-stack-trace-all", "Scan fibers then show all backtraces")
835+
]
836+
)
852837

853-
def __init__(self):
854-
super(RubyFiberScanStackTraceAllCommand, self).__init__("rb-fiber-scan-stack-trace-all", debugger.COMMAND_USER)
855-
856-
def usage(self):
857-
"""Print usage information."""
858-
print("Usage: rb-fiber-scan-stack-trace-all")
859-
print()
860-
print("Note: Run 'rb-fiber-scan-heap' first to populate the fiber cache.")
861-
862-
def invoke(self, arg, from_tty):
838+
def invoke(self, arguments, terminal):
863839
global _fiber_cache
864840

865841
# Check if cache is populated
@@ -895,7 +871,7 @@ def invoke(self, arg, from_tty):
895871

896872

897873
# Register commands
898-
RubyFiberScanHeapCommand()
899-
RubyFiberScanSwitchCommand()
900-
RubyFiberSwitchCommand()
901-
RubyFiberScanStackTraceAllCommand()
874+
debugger.register("rb-fiber-scan-heap", RubyFiberScanHeapHandler, usage=RubyFiberScanHeapHandler.USAGE)
875+
debugger.register("rb-fiber-scan-switch", RubyFiberScanSwitchHandler, usage=RubyFiberScanSwitchHandler.USAGE)
876+
debugger.register("rb-fiber-switch", RubyFiberSwitchHandler, usage=RubyFiberSwitchHandler.USAGE)
877+
debugger.register("rb-fiber-scan-stack-trace-all", RubyFiberScanStackTraceAllHandler, usage=RubyFiberScanStackTraceAllHandler.USAGE)

data/toolbox/heap.py

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import debugger
22
import sys
3+
import command
34
import constants
45

56
# Constants
@@ -471,7 +472,7 @@ def find_typed_data(self, data_type, limit=None, progress=False):
471472
return objects
472473

473474

474-
class RubyHeapScanCommand(debugger.Command):
475+
class RubyHeapScanHandler:
475476
"""Scan the Ruby heap for objects, optionally filtered by type.
476477
477478
Usage: rb-heap-scan [--type TYPE] [--limit N] [--from $heap]
@@ -501,23 +502,21 @@ class RubyHeapScanCommand(debugger.Command):
501502
rb-heap-scan --from $heap # Continue from last scan
502503
"""
503504

504-
def __init__(self):
505-
super(RubyHeapScanCommand, self).__init__("rb-heap-scan", debugger.COMMAND_USER)
506-
507-
def usage(self):
508-
"""Print usage information."""
509-
print("Usage: rb-heap-scan [--type TYPE] [--limit N] [--from $heap]")
510-
print("Examples:")
511-
print(" rb-heap-scan --type RUBY_T_STRING # Find up to 10 strings")
512-
print(" rb-heap-scan --type RUBY_T_ARRAY --limit 5 # Find up to 5 arrays")
513-
print(" rb-heap-scan --type 0x05 --limit 100 # Find up to 100 T_STRING objects")
514-
print(" rb-heap-scan --limit 20 # Scan 20 objects (any type)")
515-
print(" rb-heap-scan --type RUBY_T_STRING --from $heap # Continue from last scan")
516-
print()
517-
print("Pagination:")
518-
print(" The address of the last object is saved to $heap for pagination:")
519-
print(" rb-heap-scan --type RUBY_T_STRING --limit 10 # First page")
520-
print(" rb-heap-scan --type RUBY_T_STRING --from $heap # Next page")
505+
USAGE = command.Usage(
506+
summary="Scan the Ruby heap for objects, optionally filtered by type",
507+
parameters=[],
508+
options={
509+
'type': (str, None, 'Filter by Ruby type (e.g., RUBY_T_STRING, RUBY_T_ARRAY, or 0x05)'),
510+
'limit': (int, 10, 'Maximum objects to find'),
511+
'from': (str, None, 'Start address for pagination (use $heap)')
512+
},
513+
flags=[],
514+
examples=[
515+
("rb-heap-scan --type RUBY_T_STRING", "Find up to 10 strings"),
516+
("rb-heap-scan --type RUBY_T_ARRAY --limit 20", "Find first 20 arrays"),
517+
("rb-heap-scan --from $heap", "Continue from last scan (pagination)")
518+
]
519+
)
521520

522521
def _parse_type(self, type_arg):
523522
"""Parse a type argument and return the type value.
@@ -687,4 +686,4 @@ def invoke(self, arg, from_tty):
687686

688687

689688
# Register commands
690-
RubyHeapScanCommand()
689+
debugger.register("rb-heap-scan", RubyHeapScanHandler, usage=RubyHeapScanHandler.USAGE)

0 commit comments

Comments
 (0)