Skip to content

Commit 400a9c4

Browse files
committed
Better output formatting.
1 parent 9b8aa21 commit 400a9c4

30 files changed

+817
-185
lines changed

data/ruby/gdb/fiber.py

Lines changed: 80 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
import struct
55
import json
66
import os
7+
import sys
78

89
# Import command parser
910
import command
11+
import value
12+
import format
1013

1114
# Global cache of fibers
1215
_fiber_cache = []
@@ -74,7 +77,13 @@ def initialize(self):
7477
print("Make sure Ruby is fully initialized and the process is running.")
7578
return False
7679

77-
self.objspace = self.vm_ptr['gc']['objspace']
80+
# Ruby 3.3+ moved objspace into a gc struct
81+
try:
82+
self.objspace = self.vm_ptr['gc']['objspace']
83+
except (gdb.error, KeyError):
84+
# Ruby 3.2 and earlier have objspace directly in VM
85+
self.objspace = self.vm_ptr['objspace']
86+
7887
if int(self.objspace) == 0:
7988
print("Error: objspace is NULL")
8089
print("Make sure the Ruby GC has been initialized.")
@@ -118,8 +127,13 @@ def iterate_heap(self):
118127

119128
for i in range(allocated_pages):
120129
try:
121-
# rb_darray is a struct with 'meta' and 'data[]' fields
122-
page = self.objspace['heap_pages']['sorted']['data'][i]
130+
# Ruby 3.3+ uses rb_darray with 'data' field, Ruby 3.2- uses direct pointer
131+
try:
132+
page = self.objspace['heap_pages']['sorted']['data'][i]
133+
except (gdb.error, KeyError):
134+
# Ruby 3.2 and earlier: sorted is a direct pointer array
135+
page = self.objspace['heap_pages']['sorted'][i]
136+
123137
start = int(page['start'])
124138
total_slots = int(page['total_slots'])
125139
slot_size = int(page['slot_size'])
@@ -165,7 +179,7 @@ def find_all_fibers(self, limit=None):
165179

166180
try:
167181
allocated_pages = int(self.objspace['heap_pages']['allocated_pages'])
168-
print(f"Scanning {allocated_pages} heap pages...")
182+
print(f"Scanning {allocated_pages} heap pages...", file=sys.stderr)
169183
except (gdb.MemoryError, gdb.error) as e:
170184
print(f"Error accessing heap pages: {e}")
171185
return fibers
@@ -183,7 +197,7 @@ def find_all_fibers(self, limit=None):
183197

184198
# Print progress every 10000 objects
185199
if objects_checked % 10000 == 0:
186-
print(f" Checked {objects_checked} objects, found {len(fibers)} fiber(s)...")
200+
print(f" Checked {objects_checked} objects, found {len(fibers)} fiber(s)...", file=sys.stderr)
187201

188202
# Check if it's T_DATA
189203
if (flags & 0x1f) != self.T_DATA:
@@ -196,14 +210,14 @@ def find_all_fibers(self, limit=None):
196210
if typed_data['type'] == self.fiber_data_type:
197211
fiber_ptr = typed_data['data'].cast(self._rb_fiber_struct_type)
198212
fibers.append(fiber_ptr)
199-
print(f" Found fiber #{len(fibers)} at {fiber_ptr}")
213+
print(f" Found fiber #{len(fibers)} at {fiber_ptr}", file=sys.stderr)
200214
except (gdb.error, RuntimeError):
201215
continue
202216

203217
if limit and len(fibers) >= limit:
204-
print(f"Scan complete: checked {objects_checked} objects (stopped at limit)")
218+
print(f"Scan complete: checked {objects_checked} objects (stopped at limit)", file=sys.stderr)
205219
else:
206-
print(f"Scan complete: checked {objects_checked} objects")
220+
print(f"Scan complete: checked {objects_checked} objects", file=sys.stderr)
207221
return fibers
208222

209223
def find_fiber_by_stack(self, stack_base):
@@ -235,18 +249,9 @@ def get_fiber_info(self, fiber):
235249
errinfo_str = None
236250
try:
237251
errinfo_val = ec['errinfo']
238-
errinfo_int = int(errinfo_val)
239-
240-
# Check if errinfo is a real object (not a special constant)
241-
# Special constants: Qfalse(0x0/0x0), Qnil(0x2/0x4/0x8), Qtrue(0x6/0x14), fixnums, etc.
242-
# RB_SPECIAL_CONST_P checks: (obj == Qfalse) || RB_IMMEDIATE_P(obj)
243-
# RB_IMMEDIATE_P checks: obj & 0x03 (fixnum or special)
244-
245-
# Simple check: if low bits indicate immediate/special, skip
246-
# If it's a real object, low bits should be 0 (aligned pointer)
247-
is_special = (errinfo_int & 0x03) != 0 or errinfo_int == 0
248-
249-
if not is_special:
252+
253+
# Only process if it's a real object (not nil or other immediate value)
254+
if value.is_object(errinfo_val) and not value.is_nil(errinfo_val):
250255
errinfo = errinfo_val
251256
# Try to get exception class and message
252257
try:
@@ -256,9 +261,10 @@ def get_fiber_info(self, fiber):
256261
errinfo_str = f"{exc_class}: {exc_msg}"
257262
else:
258263
errinfo_str = exc_class
259-
except:
260-
errinfo_str = f"<exception:0x{errinfo_int:x}>"
261-
except:
264+
except Exception:
265+
# If we can't decode the exception, don't show it
266+
pass
267+
except Exception:
262268
pass
263269

264270
return {
@@ -301,8 +307,9 @@ def get_exception_class(self, exc_value):
301307
pass
302308

303309
return f"Exception(klass=0x{int(klass):x})"
304-
except:
305-
return "Exception"
310+
except Exception:
311+
# Don't return a fallback - let the caller handle it
312+
raise
306313

307314
def get_exception_message(self, exc_value):
308315
"""Get the message from an exception object.
@@ -597,7 +604,7 @@ def value_to_string(self, value):
597604
return f"<error:{e}>"
598605

599606

600-
class RubyScanFibersCommand(gdb.Command):
607+
class RubyFiberScanHeapCommand(gdb.Command):
601608
"""Scan heap and list all Ruby fibers.
602609
603610
Usage: rb-fiber-scan-heap [limit] [--cache [filename]]
@@ -608,7 +615,7 @@ class RubyScanFibersCommand(gdb.Command):
608615
"""
609616

610617
def __init__(self):
611-
super(RubyScanFibersCommand, self).__init__("rb-fiber-scan-heap", gdb.COMMAND_USER)
618+
super(RubyFiberScanHeapCommand, self).__init__("rb-fiber-scan-heap", gdb.COMMAND_USER)
612619
self.debug = RubyFiberDebug()
613620

614621
def save_cache(self, fibers, filename):
@@ -664,6 +671,9 @@ def load_cache(self, filename):
664671
def invoke(self, arg, from_tty):
665672
global _fiber_cache
666673

674+
# Create terminal for formatting
675+
terminal = format.create_terminal(from_tty)
676+
667677
# Parse arguments using the robust parser
668678
arguments = command.parse_arguments(arg if arg else "")
669679

@@ -696,14 +706,11 @@ def invoke(self, arg, from_tty):
696706
for i, fiber in enumerate(loaded_fibers):
697707
try:
698708
info = self.debug.get_fiber_info(fiber)
699-
print(f"Fiber #{i}: {info['address']}")
700-
print(f" Status: {info['status']}")
701-
print(f" Stack: base={info['stack_base']}, size={info['stack_size']}")
702-
print(f" VM Stack: {info['vm_stack']}, size={info['vm_stack_size']}")
703-
print(f" CFP: {info['cfp']}")
704-
print()
709+
self._print_fiber_info(terminal, i, info)
705710
except:
706-
print(f"Fiber #{i}: 0x{int(fiber):x} (error reading info)")
711+
print(f"Fiber #{i}: ", end='')
712+
print(terminal.print(format.metadata, '<', format.type, '0x', format.metadata, f'{int(fiber):x}>', format.reset))
713+
print(f" (error reading info)")
707714
print()
708715

709716
print(f"Fibers cached. Use 'rb-fiber <index>' to inspect a specific fiber.")
@@ -716,9 +723,9 @@ def invoke(self, arg, from_tty):
716723
return
717724

718725
if limit:
719-
print(f"Scanning heap for first {limit} Fiber object(s)...")
726+
print(f"Scanning heap for first {limit} Fiber object(s)...", file=sys.stderr)
720727
else:
721-
print("Scanning heap for Fiber objects...")
728+
print("Scanning heap for Fiber objects...", file=sys.stderr)
722729

723730
fibers = self.debug.find_all_fibers(limit=limit)
724731

@@ -732,14 +739,7 @@ def invoke(self, arg, from_tty):
732739

733740
for i, fiber in enumerate(fibers):
734741
info = self.debug.get_fiber_info(fiber)
735-
print(f"Fiber #{i}: {info['address']}")
736-
print(f" Status: {info['status']}")
737-
if info.get('errinfo_str'):
738-
print(f" Exception: {info['errinfo_str']}")
739-
print(f" Stack: base={info['stack_base']}, size={info['stack_size']}")
740-
print(f" VM Stack: {info['vm_stack']}, size={info['vm_stack_size']}")
741-
print(f" CFP: {info['cfp']}")
742-
print()
742+
self._print_fiber_info(terminal, i, info)
743743

744744
# Save to cache if requested
745745
if use_cache and fibers:
@@ -748,6 +748,36 @@ def invoke(self, arg, from_tty):
748748

749749
print(f"Fibers cached. Use 'rb-fiber <index>' to inspect a specific fiber.")
750750

751+
def _print_fiber_info(self, terminal, index, info):
752+
"""Print formatted fiber information."""
753+
# Print fiber index and address
754+
print(f"Fiber #{index}: ", end='')
755+
print(terminal.print(format.metadata, '<', format.type, '0x', format.metadata, f'{int(info["address"]):x}>', format.reset))
756+
757+
# Print status
758+
print(f" Status: {info['status']}")
759+
760+
# Print exception if present
761+
if info.get('errinfo_str'):
762+
print(f" Exception: {info['errinfo_str']}")
763+
764+
# Print Stack with formatted pointer
765+
stack_base_val = info['stack_base']
766+
stack_type = str(stack_base_val.type)
767+
print(f" Stack: ", end='')
768+
print(terminal.print_type_tag(stack_type, int(stack_base_val), f' size={info["stack_size"]}'))
769+
770+
# Print VM Stack with formatted pointer
771+
vm_stack_val = info['vm_stack']
772+
vm_stack_type = str(vm_stack_val.type)
773+
print(f" VM Stack: ", end='')
774+
print(terminal.print_type_tag(vm_stack_type, int(vm_stack_val), f' size={info["vm_stack_size"]}'))
775+
776+
# Print CFP
777+
print(f" CFP: ", end='')
778+
print(terminal.print(format.metadata, '<', format.type, '0x', format.metadata, f'{int(info["cfp"]):x}>', format.reset))
779+
print()
780+
751781

752782
class RubyFiberBacktraceCommand(gdb.Command):
753783
"""Print backtrace for a Ruby fiber.
@@ -1505,7 +1535,7 @@ def invoke(self, arg, from_tty):
15051535
if not self.debug.initialize():
15061536
return
15071537

1508-
print("Scanning heap for Fiber objects...")
1538+
print("Scanning heap for Fiber objects...", file=sys.stderr)
15091539
fibers = self.debug.find_all_fibers()
15101540

15111541
print(f"Found {len(fibers)} fiber(s)\n")
@@ -1602,7 +1632,7 @@ def invoke(self, arg, from_tty):
16021632

16031633
print(f"VM Stack for Fiber #{index}:")
16041634
print(f" Base: {info['vm_stack']}")
1605-
print(f" Size: {info['vm_stack_size']} VALUEs ({info['vm_stack_size'] * 8} bytes)")
1635+
print(f" Size: <{info['vm_stack_size']}> VALUEs (<{info['vm_stack_size'] * 8}> bytes)")
16061636
print(f" CFP: {info['cfp']}")
16071637
print()
16081638

@@ -2108,7 +2138,7 @@ def invoke(self, arg, from_tty):
21082138
search_addr = None
21092139

21102140
matches = []
2111-
print(f"\nScanning {len(_fiber_cache)} fiber(s)...\n")
2141+
print(f"\nScanning {len(_fiber_cache)} fiber(s)...\n", file=sys.stderr)
21122142

21132143
for i, fiber in enumerate(_fiber_cache):
21142144
try:
@@ -2222,7 +2252,7 @@ def invoke(self, arg, from_tty):
22222252
print(f"C/Machine Stack for Fiber #{index}:")
22232253
print(f" Fiber: {fiber}")
22242254
print(f" Stack Base: {info['stack_base']}")
2225-
print(f" Stack Size: {info['stack_size']} bytes")
2255+
print(f" Stack Size: <{info['stack_size']}> bytes")
22262256

22272257
try:
22282258
stack_start = ec['machine']['stack_start']
@@ -2231,7 +2261,7 @@ def invoke(self, arg, from_tty):
22312261

22322262
print(f" Stack Start: {stack_start}")
22332263
print(f" Stack End: {stack_end}")
2234-
print(f" Stack Maxsize: {stack_maxsize}")
2264+
print(f" Stack Maxsize: <{stack_maxsize}>")
22352265

22362266
if int(stack_start) > int(stack_end):
22372267
stack_used = int(stack_start) - int(stack_end)
@@ -2337,7 +2367,7 @@ def invoke(self, arg, from_tty):
23372367

23382368

23392369
# Register commands
2340-
RubyScanFibersCommand()
2370+
RubyFiberScanHeapCommand()
23412371
RubyFiberCommand()
23422372
RubyFiberIndexBacktraceCommand()
23432373
RubyFiberVMStackCommand()

0 commit comments

Comments
 (0)