ruby-gdb 0.1.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.
@@ -0,0 +1,426 @@
1
+ # Heap Debugging
2
+
3
+ This guide explains how to navigate Ruby's heap to find objects, diagnose memory issues, and understand object relationships.
4
+
5
+ ## Why Heap Debugging Matters
6
+
7
+ Ruby's garbage collector manages thousands to millions of objects across heap pages. When debugging memory leaks, performance issues, or trying to find specific objects, you need to navigate this heap efficiently. Standard GDB cannot understand Ruby's object layout or page structure, making manual heap inspection tedious and error-prone.
8
+
9
+ Use heap debugging when you need:
10
+
11
+ - **Find leaked objects**: Locate objects that should have been garbage collected
12
+ - **Discover fiber instances**: Find all fibers in a crashed or hung application
13
+ - **Track object relationships**: See what objects reference others
14
+ - **Diagnose memory bloat**: Understand what types of objects consume memory
15
+
16
+ ## Heap Structure Overview
17
+
18
+ Ruby's heap is organized as:
19
+
20
+ 1. **Heap Pages**: Fixed-size pages allocated from the system
21
+ 2. **Slots**: Each page contains fixed-size slots for objects
22
+ 3. **Objects**: Ruby objects (RBasic, RHash, RArray, etc.) stored in slots
23
+ 4. **Object Space**: The global heap manager (`objspace`)
24
+
25
+ ## Scanning the Heap
26
+
27
+ ### Finding All Fibers
28
+
29
+ The most common use case - find every fiber in the application:
30
+
31
+ ~~~
32
+ (gdb) rb-scan-fibers
33
+ Scanning 1250 heap pages...
34
+ Checked 45000 objects, found 5 fiber(s)...
35
+ Found fiber #1 at 0x7f8a1c800000
36
+ Found fiber #2 at 0x7f8a1c800100
37
+ ...
38
+ Scan complete: checked 67890 objects
39
+
40
+ Found 12 fiber(s):
41
+ ...
42
+ ~~~
43
+
44
+ This iterates through all heap pages and identifies fiber objects by their type metadata.
45
+
46
+ ### How Heap Scanning Works
47
+
48
+ The scanner:
49
+
50
+ 1. Accesses `ruby_current_vm_ptr->gc->objspace`
51
+ 2. Iterates through `heap_pages->sorted->data[]`
52
+ 3. For each page, checks `total_slots` objects
53
+ 4. Identifies objects by their `flags` field
54
+ 5. Filters for specific types (e.g., T_DATA with `fiber_data_type`)
55
+
56
+ ### Performance Considerations
57
+
58
+ Heap scanning can be slow in large applications:
59
+
60
+ ~~~
61
+ Scanning 5000 heap pages...
62
+ Checked 100000 objects, found 50 fiber(s)...
63
+ Checked 200000 objects, found 125 fiber(s)...
64
+ ...
65
+ ~~~
66
+
67
+ Progress updates appear every 10,000 objects. For faster subsequent access, use caching:
68
+
69
+ ~~~
70
+ (gdb) rb-scan-fibers --cache
71
+ ... (scan happens) ...
72
+ Saved 125 fiber address(es) to fibers.json
73
+
74
+ # Later (even in a new GDB session):
75
+ (gdb) rb-scan-fibers --cache
76
+ Loaded 125 fiber address(es) from fibers.json
77
+ Successfully reconstructed 125 fiber object(s)
78
+ ~~~
79
+
80
+ ## Understanding Object Layout
81
+
82
+ ### Object Headers (RBasic)
83
+
84
+ Every Ruby object starts with:
85
+
86
+ ~~~ruby
87
+ struct RBasic {
88
+ VALUE flags; # Type and flag information
89
+ VALUE klass; # Object's class
90
+ }
91
+ ~~~
92
+
93
+ The `flags` field encodes:
94
+ - Object type (bits 0-4): T_STRING, T_HASH, T_ARRAY, etc.
95
+ - GC flags (bit 5-11): Mark bits, frozen status, etc.
96
+ - Type-specific flags (bits 12+): Embedded vs heap, size info, etc.
97
+
98
+ ### Type Detection
99
+
100
+ Check an object's type:
101
+
102
+ ~~~
103
+ (gdb) set $obj = (VALUE)0x7f8a1c888888
104
+ (gdb) set $basic = (struct RBasic *)$obj
105
+ (gdb) p/x $basic->flags
106
+ $1 = 0x20040005
107
+
108
+ (gdb) p/x $basic->flags & 0x1f
109
+ $2 = 0x5 # T_STRING (0x05)
110
+ ~~~
111
+
112
+ Or use Ruby commands:
113
+
114
+ ~~~python
115
+ obj_type = flags & 0x1f
116
+ type_names = {
117
+ 0x05: "T_STRING",
118
+ 0x07: "T_ARRAY",
119
+ 0x08: "T_HASH",
120
+ 0x0c: "T_DATA",
121
+ ...
122
+ }
123
+ ~~~
124
+
125
+ ## Practical Examples
126
+
127
+ ### Finding All Objects of a Type
128
+
129
+ Scan for all string objects (requires custom script):
130
+
131
+ ~~~python
132
+ python
133
+ for obj, flags in iterate_heap():
134
+ if (flags & 0x1f) == 0x05: # T_STRING
135
+ print(f"String at {obj}")
136
+ end
137
+ ~~~
138
+
139
+ ### Locating Large Arrays
140
+
141
+ Find arrays consuming significant memory:
142
+
143
+ ~~~python
144
+ python
145
+ for obj, flags in iterate_heap():
146
+ if (flags & 0x1f) == 0x07: # T_ARRAY
147
+ rarray = obj.cast(gdb.lookup_type("struct RArray").pointer())
148
+ if (flags & (1 << 12)) == 0: # Heap array (not embedded)
149
+ length = int(rarray["as"]["heap"]["len"])
150
+ if length > 1000:
151
+ print(f"Large array at {obj}: {length} elements")
152
+ end
153
+ ~~~
154
+
155
+ ### Finding Objects by Content
156
+
157
+ Search for strings containing specific text:
158
+
159
+ ~~~python
160
+ python
161
+ import gdb
162
+
163
+ def find_strings_containing(search_text):
164
+ """Find all Ruby strings containing specific text"""
165
+ matches = []
166
+ for obj, flags in iterate_heap():
167
+ if (flags & 0x1f) == 0x05: # T_STRING
168
+ try:
169
+ rstring = obj.cast(gdb.lookup_type("struct RString").pointer())
170
+ length = int(rstring["len"])
171
+
172
+ # Read string content (simplified)
173
+ if length < 1000: # Reasonable size
174
+ # ... (read string bytes) ...
175
+ if search_text in string_content:
176
+ matches.append(obj)
177
+ except:
178
+ pass
179
+ return matches
180
+ end
181
+ ~~~
182
+
183
+ ## Memory Analysis
184
+
185
+ ### Heap Page Statistics
186
+
187
+ Understand heap organization:
188
+
189
+ ~~~
190
+ (gdb) p ruby_current_vm_ptr->gc->objspace->heap_pages->allocated_pages
191
+ $1 = 1250 # Total pages allocated
192
+
193
+ (gdb) p ruby_current_vm_ptr->gc->objspace->heap_pages->sorted->meta->length
194
+ $2 = 1250 # Accessible pages
195
+
196
+ # Average objects per page:
197
+ (gdb) p 67890 / 1250
198
+ $3 = 54 # ~54 objects per page
199
+ ~~~
200
+
201
+ ### Object Slot Details
202
+
203
+ Examine a specific heap page:
204
+
205
+ ~~~
206
+ (gdb) set $page = ruby_current_vm_ptr->gc->objspace->heap_pages->sorted->data[0]
207
+ (gdb) p $page->start # First object address
208
+ $4 = 0x7f8a1c000000
209
+
210
+ (gdb) p $page->total_slots # Objects in this page
211
+ $5 = 64
212
+
213
+ (gdb) p $page->slot_size # Bytes per slot
214
+ $6 = 40
215
+ ~~~
216
+
217
+ ### Memory Consumption
218
+
219
+ Calculate memory usage by type:
220
+
221
+ ~~~python
222
+ python
223
+ type_counts = {}
224
+ type_memory = {}
225
+
226
+ for obj, flags in iterate_heap():
227
+ obj_type = flags & 0x1f
228
+ type_counts[obj_type] = type_counts.get(obj_type, 0) + 1
229
+ # Each object consumes at minimum one slot
230
+ type_memory[obj_type] = type_memory.get(obj_type, 0) + 40 # slot_size
231
+
232
+ for obj_type in sorted(type_counts.keys()):
233
+ count = type_counts[obj_type]
234
+ memory_kb = type_memory[obj_type] / 1024
235
+ print(f"Type 0x{obj_type:02x}: {count:6} objects, {memory_kb:8.1f} KB")
236
+ end
237
+ ~~~
238
+
239
+ ## Advanced Techniques
240
+
241
+ ### Custom Heap Iterators
242
+
243
+ The fiber scanning code provides a reusable heap iterator:
244
+
245
+ ~~~python
246
+ python
247
+ # In your custom GDB script:
248
+ from fiber import RubyFiberDebug
249
+
250
+ debug = RubyFiberDebug()
251
+ if debug.initialize():
252
+ for obj, flags in debug.iterate_heap():
253
+ # Your custom logic here
254
+ pass
255
+ end
256
+ ~~~
257
+
258
+ ### Finding Objects by Reference
259
+
260
+ Locate what holds a reference to an object:
261
+
262
+ ~~~python
263
+ python
264
+ target_address = 0x7f8a1c888888
265
+
266
+ for obj, flags in iterate_heap():
267
+ obj_type = flags & 0x1f
268
+
269
+ # Check if this is a hash
270
+ if obj_type == 0x08:
271
+ rhash = obj.cast(gdb.lookup_type("struct RHash").pointer())
272
+ # ... iterate hash entries ...
273
+ # ... check if any value == target_address ...
274
+
275
+ # Check if this is an array
276
+ elif obj_type == 0x07:
277
+ # ... iterate array elements ...
278
+ end
279
+ ~~~
280
+
281
+ This helps track down unexpected object retention.
282
+
283
+ ### Heap Fragmentation Analysis
284
+
285
+ Check how objects are distributed across pages:
286
+
287
+ ~~~python
288
+ python
289
+ page_utilization = []
290
+
291
+ objspace = gdb.parse_and_eval("ruby_current_vm_ptr->gc->objspace")
292
+ allocated_pages = int(objspace["heap_pages"]["allocated_pages"])
293
+
294
+ for i in range(allocated_pages):
295
+ page = objspace["heap_pages"]["sorted"]["data"][i]
296
+ total_slots = int(page["total_slots"])
297
+
298
+ # Count non-free objects
299
+ used_slots = 0
300
+ # ... iterate and count ...
301
+
302
+ utilization = (used_slots / total_slots) * 100
303
+ page_utilization.append(utilization)
304
+
305
+ average = sum(page_utilization) / len(page_utilization)
306
+ print(f"Average page utilization: {average:.1f}%")
307
+ end
308
+ ~~~
309
+
310
+ Low utilization indicates fragmentation.
311
+
312
+ ## Best Practices
313
+
314
+ ### Cache Fiber Results
315
+
316
+ Don't scan repeatedly in the same session:
317
+
318
+ ~~~
319
+ (gdb) rb-scan-fibers --cache fibers.json # Scan once
320
+ (gdb) rb-fiber 5 # Use cached results
321
+ (gdb) rb-fiber-bt 5
322
+ ...
323
+ # Later in session:
324
+ (gdb) rb-fiber 10 # Still using cache
325
+ ~~~
326
+
327
+ ### Limit Scans in Production
328
+
329
+ For production core dumps with millions of objects:
330
+
331
+ ~~~
332
+ (gdb) rb-scan-fibers 20 # Find first 20 fibers only
333
+ ~~~
334
+
335
+ Often you only need a few fibers to diagnose issues.
336
+
337
+ ### Use Object Inspection Together
338
+
339
+ Combine heap scanning with object inspection:
340
+
341
+ ~~~
342
+ (gdb) rb-scan-fibers
343
+ Fiber #5 has exception: IOError
344
+
345
+ (gdb) rb-fiber 5
346
+ (gdb) set $ec = ... # (shown in output)
347
+ (gdb) rb-object-print $ec->errinfo --depth 3
348
+ ~~~
349
+
350
+ ## Common Pitfalls
351
+
352
+ ### Stale Cache Files
353
+
354
+ If you load a cache from a different core dump:
355
+
356
+ ~~~
357
+ (gdb) rb-scan-fibers --cache
358
+ Loaded 125 fiber address(es) from fibers.json
359
+ Warning: Could not access fiber at 0x7f8a1c800500
360
+ Warning: Could not access fiber at 0x7f8a1c800600
361
+ ...
362
+ ~~~
363
+
364
+ Delete the cache and rescan:
365
+
366
+ ~~~ bash
367
+ $ rm fibers.json
368
+ ~~~
369
+
370
+ ### Scanning Uninitialized VM
371
+
372
+ If you scan too early during Ruby initialization:
373
+
374
+ ~~~
375
+ (gdb) rb-scan-fibers
376
+ Error: ruby_current_vm_ptr is NULL
377
+ Make sure Ruby is fully initialized and the process is running.
378
+ ~~~
379
+
380
+ Set a breakpoint after VM initialization:
381
+
382
+ ~~~
383
+ (gdb) break rb_vm_exec
384
+ (gdb) run
385
+ (gdb) rb-scan-fibers # Now works
386
+ ~~~
387
+
388
+ ### Memory Errors in Core Dumps
389
+
390
+ Some heap pages may be unmapped in core dumps:
391
+
392
+ ~~~
393
+ Scanning 5000 heap pages...
394
+ Error reading page 1234: Cannot access memory at address 0x...
395
+ ~~~
396
+
397
+ This is normal - continue with accessible pages.
398
+
399
+ ## Performance Optimization
400
+
401
+ ### Targeted Scanning
402
+
403
+ Instead of scanning the entire heap, use known addresses:
404
+
405
+ ~~~
406
+ (gdb) rb-fiber-from-stack 0x7f8a1e000000
407
+ Searching for fiber with stack base 0x7f8a1e000000...
408
+ Found fiber: 0x7f8a1c800300
409
+ ~~~
410
+
411
+ ### Heap Iteration Caching
412
+
413
+ The scanner caches GDB type lookups for performance:
414
+
415
+ - `struct RBasic`
416
+ - `struct RTypedData`
417
+ - `struct rb_fiber_struct`
418
+
419
+ This makes subsequent iterations much faster.
420
+
421
+ ## See Also
422
+
423
+ - {ruby Ruby::GDB::fiber-debugging Fiber debugging} for working with found fibers
424
+ - {ruby Ruby::GDB::object-inspection Object inspection} for examining heap objects
425
+ - {ruby Ruby::GDB::stack-inspection Stack inspection} for understanding object references
426
+
@@ -0,0 +1,28 @@
1
+ # Automatically generated context index for Utopia::Project guides.
2
+ # Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
3
+ ---
4
+ description: Ruby debugging extensions for GDB
5
+ metadata:
6
+ documentation_uri: https://socketry.github.io/ruby-gdb/
7
+ source_code_uri: https://github.com/socketry/ruby-gdb
8
+ files:
9
+ - path: getting-started.md
10
+ title: Getting Started
11
+ description: This guide explains how to install and use Ruby GDB extensions for
12
+ debugging Ruby programs and core dumps.
13
+ - path: object-inspection.md
14
+ title: Object Inspection
15
+ description: This guide explains how to use `rb-object-print` to inspect Ruby objects,
16
+ hashes, arrays, and structs in GDB.
17
+ - path: stack-inspection.md
18
+ title: Stack Inspection
19
+ description: This guide explains how to inspect both Ruby VM stacks and native C
20
+ stacks when debugging Ruby programs.
21
+ - path: fiber-debugging.md
22
+ title: Fiber Debugging
23
+ description: This guide explains how to debug Ruby fibers using GDB, including inspecting
24
+ fiber state, backtraces, and switching between fiber contexts.
25
+ - path: heap-debugging.md
26
+ title: Heap Debugging
27
+ description: This guide explains how to navigate Ruby's heap to find objects, diagnose
28
+ memory issues, and understand object relationships.
@@ -0,0 +1,272 @@
1
+ # Object Inspection
2
+
3
+ This guide explains how to use `rb-object-print` to inspect Ruby objects, hashes, arrays, and structs in GDB.
4
+
5
+ ## Why Object Inspection Matters
6
+
7
+ When debugging Ruby programs or analyzing core dumps, you often need to inspect complex data structures that are difficult to read in their raw memory representation. Standard GDB commands show pointer addresses and raw memory, but not the logical structure of Ruby objects.
8
+
9
+ Use `rb-object-print` when you need:
10
+
11
+ - **Understand exception objects**: See the full exception hierarchy, message, and backtrace data
12
+ - **Inspect fiber storage**: View thread-local data and fiber-specific variables
13
+ - **Debug data corruption**: Check the contents of hashes and arrays for unexpected values
14
+ - **Analyze VM state**: Examine objects on the VM stack without manual pointer arithmetic
15
+
16
+ ## Basic Usage
17
+
18
+ The `rb-object-print` command recursively prints Ruby objects in a human-readable format.
19
+
20
+ ### Syntax
21
+
22
+ ~~~
23
+ rb-object-print <expression> [--depth N] [--debug]
24
+ ~~~
25
+
26
+ Where:
27
+ - `<expression>`: Any GDB expression that evaluates to a Ruby VALUE
28
+ - `--depth N`: Maximum recursion depth (default: 1)
29
+ - `--debug`: Enable diagnostic output for troubleshooting
30
+
31
+ ### Simple Values
32
+
33
+ Print immediate values and special constants:
34
+
35
+ ~~~
36
+ (gdb) rb-object-print 0 # Qfalse
37
+ (gdb) rb-object-print 8 # Qnil
38
+ (gdb) rb-object-print 20 # Qtrue
39
+ (gdb) rb-object-print 85 # Fixnum: 42
40
+ ~~~
41
+
42
+ These work without any Ruby process running, making them useful for learning and testing.
43
+
44
+ ### Expressions
45
+
46
+ Use any valid GDB expression:
47
+
48
+ ~~~
49
+ (gdb) rb-object-print $ec->errinfo # Exception object
50
+ (gdb) rb-object-print $ec->cfp->sp[-1] # Top of VM stack
51
+ (gdb) rb-object-print $ec->storage # Fiber storage hash
52
+ (gdb) rb-object-print (VALUE)0x00007f8a12345678 # Object at specific address
53
+ ~~~
54
+
55
+ ## Inspecting Hashes
56
+
57
+ Ruby hashes have two internal representations (ST table and AR table). The command automatically detects and displays both:
58
+
59
+ ### Small Hashes (AR Table)
60
+
61
+ For hashes with fewer than 8 entries, Ruby uses an array-based implementation:
62
+
63
+ ~~~
64
+ (gdb) rb-object-print $some_hash
65
+ AR Table at 0x7f8a1c123456 (size=4, bound=3):
66
+ [ 0] K: :name
67
+ V: "Alice"
68
+ [ 1] K: :age
69
+ V: Fixnum: 30
70
+ [ 2] K: :active
71
+ V: Qtrue
72
+ ~~~
73
+
74
+ ### Large Hashes (ST Table)
75
+
76
+ For larger hashes, Ruby uses a hash table:
77
+
78
+ ~~~
79
+ (gdb) rb-object-print $large_hash
80
+ ST Table at 0x7f8a1c789abc (15 entries):
81
+ [ 0] K: :user_id
82
+ V: Fixnum: 12345
83
+ [ 1] K: :session_data
84
+ V: <RString>
85
+ ...
86
+ ~~~
87
+
88
+ ### Controlling Depth
89
+
90
+ Prevent overwhelming output from deeply nested structures:
91
+
92
+ ~~~
93
+ (gdb) rb-object-print $nested_hash --depth 1 # Only top level
94
+ (gdb) rb-object-print $nested_hash --depth 2 # One level of nesting
95
+ (gdb) rb-object-print $nested_hash --depth 5 # Deep inspection
96
+ ~~~
97
+
98
+ At depth 1, nested hashes/arrays show as `<RHash>` or `<RArray>`. Increase depth to expand them.
99
+
100
+ ## Inspecting Arrays
101
+
102
+ Arrays also have two representations based on size:
103
+
104
+ ### Embedded Arrays
105
+
106
+ Small arrays (up to 3 elements on 64-bit) are stored inline:
107
+
108
+ ~~~
109
+ (gdb) rb-object-print $small_array
110
+ Embedded Array at 0x7f8a1c234567 (length 3)
111
+ [ 0] I: Fixnum: 1
112
+ [ 1] I: Fixnum: 2
113
+ [ 2] I: Fixnum: 3
114
+ ~~~
115
+
116
+ ### Heap Arrays
117
+
118
+ Larger arrays allocate separate memory:
119
+
120
+ ~~~
121
+ (gdb) rb-object-print $big_array --depth 2
122
+ Heap Array at 0x7f8a1c345678 (length 100)
123
+ [ 0] I: Fixnum: 1
124
+ [ 1] I: "first item"
125
+ ...
126
+ ~~~
127
+
128
+ ## Inspecting Structs
129
+
130
+ Ruby Struct objects work similarly to arrays:
131
+
132
+ ~~~
133
+ (gdb) rb-object-print $struct_instance
134
+ Heap Struct at 0x7f8a1c456789 (length 4)
135
+ [ 0] I: "John"
136
+ [ 1] I: Fixnum: 25
137
+ [ 2] I: "Engineer"
138
+ [ 3] I: Qtrue
139
+ ~~~
140
+
141
+ ## Practical Examples
142
+
143
+ ### Debugging Exception in Fiber
144
+
145
+ When a fiber has an exception, inspect it:
146
+
147
+ ~~~
148
+ (gdb) rb-scan-fibers
149
+ (gdb) rb-fiber 5 # Shows fiber with exception
150
+ (gdb) set $ec = ... # (shown in output)
151
+ (gdb) rb-object-print $ec->errinfo --depth 3
152
+ ~~~
153
+
154
+ This reveals the full exception structure including any nested causes.
155
+
156
+ ### Inspecting Method Arguments
157
+
158
+ Break at a method and examine arguments on the stack:
159
+
160
+ ~~~
161
+ (gdb) break some_method
162
+ (gdb) run
163
+ (gdb) rb-object-print $ec->cfp->sp[-1] # Last argument
164
+ (gdb) rb-object-print $ec->cfp->sp[-2] # Second-to-last argument
165
+ ~~~
166
+
167
+ ### Examining Fiber Storage
168
+
169
+ Thread-local variables are stored in fiber storage:
170
+
171
+ ~~~
172
+ (gdb) rb-scan-fibers
173
+ (gdb) rb-fiber 0
174
+ (gdb) rb-object-print $ec->storage --depth 2
175
+ ~~~
176
+
177
+ This shows all thread-local variables and their values.
178
+
179
+ ## Debugging with --debug Flag
180
+
181
+ When `rb-object-print` doesn't show what you expect, use `--debug`:
182
+
183
+ ~~~
184
+ (gdb) rb-object-print $suspicious_value --debug
185
+ DEBUG: Evaluated '$suspicious_value' to 0x7f8a1c567890
186
+ DEBUG: Loaded constant RUBY_T_MASK = 31
187
+ DEBUG: Object at 0x7f8a1c567890 with flags=0x20040005, type=0x5
188
+ ...
189
+ ~~~
190
+
191
+ This shows:
192
+ - How the expression was evaluated
193
+ - What constants were loaded
194
+ - Object type detection logic
195
+ - Any errors encountered
196
+
197
+ Use this to troubleshoot:
198
+ - Unexpected output format
199
+ - Missing nested structures
200
+ - Type detection issues
201
+ - Access errors
202
+
203
+ ## Best Practices
204
+
205
+ ### Choose Appropriate Depth
206
+
207
+ - **Depth 1**: Quick overview, minimal output (default)
208
+ - **Depth 2-3**: Common for most debugging tasks
209
+ - **Depth 5+**: Only for deep investigation, can be verbose
210
+
211
+ ### Work with Core Dumps
212
+
213
+ The command works perfectly with core dumps since it only reads memory:
214
+
215
+ ~~~
216
+ $ gdb ruby core.12345
217
+ (gdb) source ~/.local/share/gdb/ruby/init.gdb
218
+ (gdb) rb-object-print $ec->errinfo
219
+ ~~~
220
+
221
+ No running process needed!
222
+
223
+ ### Combine with Standard GDB
224
+
225
+ Use standard GDB commands alongside Ruby extensions:
226
+
227
+ ~~~
228
+ (gdb) info locals # See C variables
229
+ (gdb) rb-object-print $val # Interpret as Ruby object
230
+ (gdb) x/10gx $ec->cfp->sp # Raw memory view
231
+ ~~~
232
+
233
+ ## Common Pitfalls
234
+
235
+ ### Accessing Deallocated Objects
236
+
237
+ If an object address is invalid, you'll see an error:
238
+
239
+ ~~~
240
+ (gdb) rb-object-print (VALUE)0xdeadbeef
241
+ [Error printing object: Cannot access memory at address 0xdeadbeef]
242
+ ~~~
243
+
244
+ Always verify the address is valid before inspecting.
245
+
246
+ ### Depth Too Low
247
+
248
+ If you see `<RHash>` or `<RArray>` where you expected expanded content:
249
+
250
+ ~~~
251
+ (gdb) rb-object-print $hash # Shows: <RHash>
252
+ (gdb) rb-object-print $hash --depth 2 # Shows actual content
253
+ ~~~
254
+
255
+ Increase `--depth` to see nested structures.
256
+
257
+ ### Missing Debug Symbols
258
+
259
+ Without debug symbols, some type information may be unavailable:
260
+
261
+ ~~~
262
+ Python Exception <class 'gdb.error'>: No type named RBasic
263
+ ~~~
264
+
265
+ Solution: Install Ruby with debug symbols or use a debug build.
266
+
267
+ ## See Also
268
+
269
+ - {ruby Ruby::GDB::fiber-debugging Fiber debugging} for inspecting fiber-specific data
270
+ - {ruby Ruby::GDB::stack-inspection Stack inspection} for examining call frames
271
+ - {ruby Ruby::GDB::heap-debugging Heap debugging} for scanning objects
272
+