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
data/data/toolbox/rbignum.py
CHANGED
|
@@ -31,15 +31,11 @@ class RBignumObject:
|
|
|
31
31
|
return f"<T_BIGNUM@0x{addr:x} heap length={len(self)}>"
|
|
32
32
|
|
|
33
33
|
def print_to(self, terminal):
|
|
34
|
-
"""
|
|
34
|
+
"""Print formatted bignum representation."""
|
|
35
35
|
addr = int(self.value)
|
|
36
36
|
storage = "embedded" if self.is_embedded() else "heap"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
format.type, 'T_BIGNUM',
|
|
40
|
-
format.metadata, f'@0x{addr:x} {storage} length={len(self)}>',
|
|
41
|
-
format.reset
|
|
42
|
-
)
|
|
37
|
+
details = f"{storage} length={len(self)}"
|
|
38
|
+
terminal.print_type_tag('T_BIGNUM', addr, details)
|
|
43
39
|
|
|
44
40
|
def print_recursive(self, printer, depth):
|
|
45
41
|
"""Print this bignum (no recursion needed)."""
|
data/data/toolbox/rclass.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import debugger
|
|
2
2
|
import constants
|
|
3
|
-
import
|
|
3
|
+
import rvalue
|
|
4
4
|
import rstring
|
|
5
5
|
|
|
6
6
|
class RClass:
|
|
@@ -86,13 +86,13 @@ class RClass:
|
|
|
86
86
|
|
|
87
87
|
# Strategy 2: Try modern rb_classext_struct.classpath (Ruby 3.4+)
|
|
88
88
|
try:
|
|
89
|
-
rclass = self.klass.cast(
|
|
89
|
+
rclass = self.klass.cast(constants.type_struct('struct RClass').pointer())
|
|
90
90
|
# Try to access classext.classpath
|
|
91
91
|
try:
|
|
92
92
|
# Try embedded classext (RCLASS_EXT_EMBEDDED)
|
|
93
93
|
rclass_size = debugger.parse_and_eval("sizeof(struct RClass)")
|
|
94
94
|
classext_addr = int(self.klass) + int(rclass_size)
|
|
95
|
-
classext_type =
|
|
95
|
+
classext_type = constants.type_struct('rb_classext_t')
|
|
96
96
|
classext_ptr = debugger.create_value_from_address(classext_addr, classext_type).address
|
|
97
97
|
classpath_val = classext_ptr['classpath']
|
|
98
98
|
except:
|
|
@@ -103,9 +103,9 @@ class RClass:
|
|
|
103
103
|
except:
|
|
104
104
|
classpath_val = None
|
|
105
105
|
|
|
106
|
-
if classpath_val and int(classpath_val) != 0 and not
|
|
106
|
+
if classpath_val and int(classpath_val) != 0 and not rvalue.is_nil(classpath_val):
|
|
107
107
|
# Decode the classpath string
|
|
108
|
-
class_name_obj =
|
|
108
|
+
class_name_obj = rvalue.interpret(classpath_val)
|
|
109
109
|
if hasattr(class_name_obj, 'to_str'):
|
|
110
110
|
class_name = class_name_obj.to_str()
|
|
111
111
|
if class_name and not class_name.startswith('<'):
|
data/data/toolbox/readme.md
CHANGED
|
@@ -14,7 +14,7 @@ data/toolbox/
|
|
|
14
14
|
│ └── *.md # Documentation
|
|
15
15
|
│
|
|
16
16
|
# Ruby debugging extensions (currently GDB-specific, migrating to abstraction)
|
|
17
|
-
├── object.py # Object inspection (rb-
|
|
17
|
+
├── object.py # Object inspection (rb-print)
|
|
18
18
|
├── fiber.py # Fiber debugging (rb-fiber-scan-heap, rb-fiber-switch)
|
|
19
19
|
├── heap.py # Heap scanning (rb-heap-scan)
|
|
20
20
|
├── stack.py # Stack inspection
|
|
@@ -89,7 +89,7 @@ value_cast = value.cast(type.pointer())
|
|
|
89
89
|
### GDB
|
|
90
90
|
```bash
|
|
91
91
|
gem install toolbox
|
|
92
|
-
bake toolbox:gdb:install
|
|
92
|
+
bake -g toolbox toolbox:gdb:install
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
This adds to `~/.gdbinit`:
|
|
@@ -101,7 +101,7 @@ source /path/to/gem/data/toolbox/init.py
|
|
|
101
101
|
### LLDB
|
|
102
102
|
```bash
|
|
103
103
|
gem install toolbox
|
|
104
|
-
bake toolbox:lldb:install
|
|
104
|
+
bake -g toolbox toolbox:lldb:install
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
This adds to `~/.lldbinit`:
|
|
@@ -113,7 +113,7 @@ command script import /path/to/gem/data/toolbox/init.py
|
|
|
113
113
|
## Available Commands
|
|
114
114
|
|
|
115
115
|
### Object Inspection
|
|
116
|
-
- `rb-
|
|
116
|
+
- `rb-print <expression> [--depth N]` - Print Ruby objects with recursion
|
|
117
117
|
|
|
118
118
|
### Fiber Debugging
|
|
119
119
|
- `rb-fiber-scan-heap [--limit N]` - Scan heap for fiber objects
|
|
@@ -125,7 +125,7 @@ command script import /path/to/gem/data/toolbox/init.py
|
|
|
125
125
|
- `rb-heap-scan [--type TYPE] [--limit N]` - Scan Ruby heap for objects
|
|
126
126
|
|
|
127
127
|
### Stack Inspection
|
|
128
|
-
- `rb-stack-
|
|
128
|
+
- `rb-stack-trace` - Print combined Ruby/C stack trace
|
|
129
129
|
|
|
130
130
|
## Migration Status
|
|
131
131
|
|
|
@@ -151,14 +151,14 @@ command script import /path/to/gem/data/toolbox/init.py
|
|
|
151
151
|
Test with GDB:
|
|
152
152
|
```bash
|
|
153
153
|
gdb -q ruby
|
|
154
|
-
(gdb) help rb-
|
|
154
|
+
(gdb) help rb-print
|
|
155
155
|
(gdb) help rb-fiber-scan-heap
|
|
156
156
|
```
|
|
157
157
|
|
|
158
158
|
Test with LLDB (once migrated):
|
|
159
159
|
```bash
|
|
160
160
|
lldb ruby
|
|
161
|
-
(lldb) help rb-
|
|
161
|
+
(lldb) help rb-print
|
|
162
162
|
(lldb) help rb-fiber-scan-heap
|
|
163
163
|
```
|
|
164
164
|
|
|
@@ -192,7 +192,7 @@ if toolbox_dir not in sys.path:
|
|
|
192
192
|
This allows:
|
|
193
193
|
```python
|
|
194
194
|
import debugger # data/toolbox/debugger.py
|
|
195
|
-
import
|
|
195
|
+
import print # data/toolbox/print.py
|
|
196
196
|
import fiber # data/toolbox/fiber.py
|
|
197
197
|
from debugger import gdb # data/toolbox/debugger/gdb.py
|
|
198
198
|
```
|
data/data/toolbox/rexception.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import debugger
|
|
2
2
|
import constants
|
|
3
3
|
import format
|
|
4
|
-
import
|
|
4
|
+
import rvalue
|
|
5
5
|
import rstring
|
|
6
6
|
import rclass
|
|
7
7
|
|
|
@@ -21,7 +21,7 @@ class RException:
|
|
|
21
21
|
self._message = None
|
|
22
22
|
|
|
23
23
|
# Validate it's an object
|
|
24
|
-
if
|
|
24
|
+
if rvalue.is_immediate(exception_value):
|
|
25
25
|
raise ValueError("Exception VALUE cannot be an immediate value")
|
|
26
26
|
|
|
27
27
|
@property
|
|
@@ -116,7 +116,7 @@ def is_exception(val):
|
|
|
116
116
|
Returns:
|
|
117
117
|
True if the value appears to be an exception object, False otherwise
|
|
118
118
|
"""
|
|
119
|
-
if not
|
|
119
|
+
if not rvalue.is_object(val):
|
|
120
120
|
return False
|
|
121
121
|
|
|
122
122
|
try:
|
data/data/toolbox/rfloat.py
CHANGED
|
@@ -32,15 +32,10 @@ class RFloatImmediate:
|
|
|
32
32
|
return f"<T_FLOAT> {self.float_value()}"
|
|
33
33
|
|
|
34
34
|
def print_to(self, terminal):
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
format.metadata, '>',
|
|
40
|
-
format.reset
|
|
41
|
-
)
|
|
42
|
-
num_val = terminal.print(format.number, str(self.float_value()), format.reset)
|
|
43
|
-
return f"{tag} {num_val}"
|
|
35
|
+
"""Print formatted float representation."""
|
|
36
|
+
terminal.print_type_tag('T_FLOAT')
|
|
37
|
+
terminal.print(' ', end='')
|
|
38
|
+
terminal.print(format.number, str(self.float_value()), format.reset, end='')
|
|
44
39
|
|
|
45
40
|
def print_recursive(self, printer, depth):
|
|
46
41
|
"""Print this float (no recursion needed)."""
|
|
@@ -59,16 +54,11 @@ class RFloatObject:
|
|
|
59
54
|
return f"<T_FLOAT@0x{addr:x}> {self.float_value()}"
|
|
60
55
|
|
|
61
56
|
def print_to(self, terminal):
|
|
62
|
-
"""
|
|
57
|
+
"""Print formatted float representation."""
|
|
63
58
|
addr = int(self.value.address)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
format.metadata, f'@0x{addr:x}>',
|
|
68
|
-
format.reset
|
|
69
|
-
)
|
|
70
|
-
num_val = terminal.print(format.number, str(self.float_value()), format.reset)
|
|
71
|
-
return f"{tag} {num_val}"
|
|
59
|
+
terminal.print_type_tag('T_FLOAT', addr)
|
|
60
|
+
terminal.print(' ', end='')
|
|
61
|
+
terminal.print(format.number, str(self.float_value()), format.reset, end='')
|
|
72
62
|
|
|
73
63
|
def print_recursive(self, printer, depth):
|
|
74
64
|
"""Print this float (no recursion needed)."""
|
data/data/toolbox/rhash.py
CHANGED
|
@@ -51,12 +51,8 @@ class RHashSTTable(RHashBase):
|
|
|
51
51
|
def print_to(self, terminal):
|
|
52
52
|
"""Print this hash with formatting."""
|
|
53
53
|
addr = int(self.value)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
format.type, 'T_HASH',
|
|
57
|
-
format.metadata, f'@0x{addr:x} ST-Table entries={self.size()}>',
|
|
58
|
-
format.reset
|
|
59
|
-
)
|
|
54
|
+
details = f"ST-Table entries={self.size()}"
|
|
55
|
+
terminal.print_type_tag('T_HASH', addr, details)
|
|
60
56
|
|
|
61
57
|
def print_recursive(self, printer, depth):
|
|
62
58
|
"""Print this hash recursively."""
|
|
@@ -121,12 +117,8 @@ class RHashARTable(RHashBase):
|
|
|
121
117
|
def print_to(self, terminal):
|
|
122
118
|
"""Print this hash with formatting."""
|
|
123
119
|
addr = int(self.value)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
format.type, 'T_HASH',
|
|
127
|
-
format.metadata, f'@0x{addr:x} AR-Table size={self.size()} bound={self.bound()}>',
|
|
128
|
-
format.reset
|
|
129
|
-
)
|
|
120
|
+
details = f"AR-Table size={self.size()} bound={self.bound()}"
|
|
121
|
+
terminal.print_type_tag('T_HASH', addr, details)
|
|
130
122
|
|
|
131
123
|
def print_recursive(self, printer, depth):
|
|
132
124
|
"""Print this hash recursively."""
|
data/data/toolbox/rstring.py
CHANGED
|
@@ -71,15 +71,11 @@ class RStringBase:
|
|
|
71
71
|
addr = int(self.value)
|
|
72
72
|
storage = "embedded" if self._is_embedded() else "heap"
|
|
73
73
|
content = self.to_str()
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
format.metadata, f'@0x{addr:x} {storage} length={self.length()}>',
|
|
78
|
-
format.reset
|
|
79
|
-
)
|
|
74
|
+
details = f"{storage} length={self.length()}"
|
|
75
|
+
terminal.print_type_tag('T_STRING', addr, details)
|
|
76
|
+
terminal.print(' ', end='')
|
|
80
77
|
# Use repr() to properly escape quotes, newlines, etc.
|
|
81
|
-
|
|
82
|
-
return f"{tag} {string_val}"
|
|
78
|
+
terminal.print(format.string, repr(content), format.reset, end='')
|
|
83
79
|
|
|
84
80
|
def print_recursive(self, printer, depth):
|
|
85
81
|
"""Print this string (no recursion needed for strings)."""
|
data/data/toolbox/rstruct.py
CHANGED
|
@@ -60,14 +60,10 @@ class RStructEmbedded(RStructBase):
|
|
|
60
60
|
return f"<T_STRUCT@0x{addr:x} embedded length={len(self)}>"
|
|
61
61
|
|
|
62
62
|
def print_to(self, terminal):
|
|
63
|
-
"""
|
|
63
|
+
"""Print formatted struct representation."""
|
|
64
64
|
addr = int(self.value)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
format.type, 'T_STRUCT',
|
|
68
|
-
format.metadata, f'@0x{addr:x} embedded length={len(self)}>',
|
|
69
|
-
format.reset
|
|
70
|
-
)
|
|
65
|
+
details = f"embedded length={len(self)}"
|
|
66
|
+
terminal.print_type_tag('T_STRUCT', addr, details)
|
|
71
67
|
|
|
72
68
|
def print_recursive(self, printer, depth):
|
|
73
69
|
"""Print this struct recursively."""
|
|
@@ -101,14 +97,10 @@ class RStructHeap(RStructBase):
|
|
|
101
97
|
return f"<T_STRUCT@0x{addr:x} heap length={len(self)}>"
|
|
102
98
|
|
|
103
99
|
def print_to(self, terminal):
|
|
104
|
-
"""
|
|
100
|
+
"""Print formatted struct representation."""
|
|
105
101
|
addr = int(self.value)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
format.type, 'T_STRUCT',
|
|
109
|
-
format.metadata, f'@0x{addr:x} heap length={len(self)}>',
|
|
110
|
-
format.reset
|
|
111
|
-
)
|
|
102
|
+
details = f"heap length={len(self)}"
|
|
103
|
+
terminal.print_type_tag('T_STRUCT', addr, details)
|
|
112
104
|
|
|
113
105
|
def print_recursive(self, printer, depth):
|
|
114
106
|
"""Print this struct recursively."""
|
data/data/toolbox/rsymbol.py
CHANGED
|
@@ -138,25 +138,13 @@ class RSymbolImmediate:
|
|
|
138
138
|
|
|
139
139
|
def print_to(self, terminal):
|
|
140
140
|
"""Return formatted symbol representation."""
|
|
141
|
+
terminal.print_type_tag('T_SYMBOL')
|
|
142
|
+
terminal.print(' ', end='')
|
|
141
143
|
name = self.to_str()
|
|
142
144
|
if name:
|
|
143
|
-
|
|
144
|
-
format.metadata, '<',
|
|
145
|
-
format.type, 'T_SYMBOL',
|
|
146
|
-
format.metadata, '>',
|
|
147
|
-
format.reset
|
|
148
|
-
)
|
|
149
|
-
symbol_val = terminal.print(format.symbol, f':{name}', format.reset)
|
|
150
|
-
return f"{tag} {symbol_val}"
|
|
145
|
+
terminal.print(format.symbol, f':{name}', format.reset, end='')
|
|
151
146
|
else:
|
|
152
|
-
|
|
153
|
-
format.metadata, '<',
|
|
154
|
-
format.type, 'T_SYMBOL',
|
|
155
|
-
format.metadata, '>',
|
|
156
|
-
format.reset
|
|
157
|
-
)
|
|
158
|
-
symbol_val = terminal.print(format.symbol, f':id_0x{self.id():x}', format.reset)
|
|
159
|
-
return f"{tag} {symbol_val}"
|
|
147
|
+
terminal.print(format.symbol, f':id_0x{self.id():x}', format.reset, end='')
|
|
160
148
|
|
|
161
149
|
def print_recursive(self, printer, depth):
|
|
162
150
|
"""Print this symbol (no recursion needed)."""
|
|
@@ -208,28 +196,16 @@ class RSymbolObject:
|
|
|
208
196
|
return f"<T_SYMBOL@0x{addr:x}> :<Symbol:0x{int(fstr_val):x}>"
|
|
209
197
|
|
|
210
198
|
def print_to(self, terminal):
|
|
211
|
-
"""
|
|
199
|
+
"""Print formatted symbol representation."""
|
|
212
200
|
name = self.to_str()
|
|
213
201
|
addr = int(self.value)
|
|
202
|
+
terminal.print_type_tag('T_SYMBOL', addr)
|
|
203
|
+
terminal.print(' ', end='')
|
|
214
204
|
if name:
|
|
215
|
-
|
|
216
|
-
format.metadata, '<',
|
|
217
|
-
format.type, 'T_SYMBOL',
|
|
218
|
-
format.metadata, f'@0x{addr:x}>',
|
|
219
|
-
format.reset
|
|
220
|
-
)
|
|
221
|
-
symbol_val = terminal.print(format.symbol, f':{name}', format.reset)
|
|
222
|
-
return f"{tag} {symbol_val}"
|
|
205
|
+
terminal.print(format.symbol, f':{name}', format.reset, end='')
|
|
223
206
|
else:
|
|
224
207
|
fstr_val = self.fstr()
|
|
225
|
-
|
|
226
|
-
format.metadata, '<',
|
|
227
|
-
format.type, 'T_SYMBOL',
|
|
228
|
-
format.metadata, f'@0x{addr:x}>',
|
|
229
|
-
format.reset
|
|
230
|
-
)
|
|
231
|
-
symbol_val = terminal.print(format.symbol, f':<Symbol:0x{int(fstr_val):x}>', format.reset)
|
|
232
|
-
return f"{tag} {symbol_val}"
|
|
208
|
+
terminal.print(format.symbol, f':<Symbol:0x{int(fstr_val):x}>', format.reset, end='')
|
|
233
209
|
|
|
234
210
|
def print_recursive(self, printer, depth):
|
|
235
211
|
"""Print this symbol (no recursion needed)."""
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import sys
|
|
2
2
|
import constants
|
|
3
3
|
import format
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
import rbasic
|
|
6
7
|
import rfloat
|
|
7
8
|
import rsymbol
|
|
@@ -35,19 +36,19 @@ class RImmediate:
|
|
|
35
36
|
def print_to(self, terminal):
|
|
36
37
|
"""Print this value with formatting to the given terminal."""
|
|
37
38
|
if self.val_int == 0:
|
|
38
|
-
|
|
39
|
+
terminal.print_type_tag('T_FALSE')
|
|
39
40
|
elif self.val_int == 0x04 or self.val_int == 0x08:
|
|
40
|
-
|
|
41
|
+
terminal.print_type_tag('T_NIL')
|
|
41
42
|
elif self.val_int == 0x14:
|
|
42
|
-
|
|
43
|
+
terminal.print_type_tag('T_TRUE')
|
|
43
44
|
elif (self.val_int & 0x01) != 0:
|
|
44
45
|
# Fixnum - shift right to get actual value
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
terminal.print_type_tag('T_FIXNUM')
|
|
47
|
+
terminal.print(' ', end='')
|
|
48
|
+
terminal.print(format.number, str(self.val_int >> 1), format.reset, end='')
|
|
48
49
|
else:
|
|
49
50
|
# Unknown immediate
|
|
50
|
-
|
|
51
|
+
terminal.print_type_tag('Immediate', self.val_int)
|
|
51
52
|
|
|
52
53
|
def print_recursive(self, printer, depth):
|
|
53
54
|
"""Print this immediate value (no recursion needed)."""
|
|
@@ -138,7 +139,6 @@ def interpret(value):
|
|
|
138
139
|
Returns:
|
|
139
140
|
An instance of the appropriate type class (never None)
|
|
140
141
|
"""
|
|
141
|
-
import sys
|
|
142
142
|
val_int = int(value)
|
|
143
143
|
|
|
144
144
|
# Check for immediate flonum (must be before fixnum check)
|