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.
- checksums.yaml +7 -0
- data/bake/ruby/gdb.rb +135 -0
- data/context/fiber-debugging.md +372 -0
- data/context/getting-started.md +167 -0
- data/context/heap-debugging.md +426 -0
- data/context/index.yaml +28 -0
- data/context/object-inspection.md +272 -0
- data/context/stack-inspection.md +357 -0
- data/data/ruby/gdb/command.py +254 -0
- data/data/ruby/gdb/constants.py +59 -0
- data/data/ruby/gdb/fiber.py +825 -0
- data/data/ruby/gdb/format.py +201 -0
- data/data/ruby/gdb/heap.py +563 -0
- data/data/ruby/gdb/init.py +25 -0
- data/data/ruby/gdb/object.py +85 -0
- data/data/ruby/gdb/rarray.py +124 -0
- data/data/ruby/gdb/rbasic.py +103 -0
- data/data/ruby/gdb/rbignum.py +52 -0
- data/data/ruby/gdb/rclass.py +133 -0
- data/data/ruby/gdb/rexception.py +150 -0
- data/data/ruby/gdb/rfloat.py +95 -0
- data/data/ruby/gdb/rhash.py +157 -0
- data/data/ruby/gdb/rstring.py +217 -0
- data/data/ruby/gdb/rstruct.py +157 -0
- data/data/ruby/gdb/rsymbol.py +291 -0
- data/data/ruby/gdb/stack.py +609 -0
- data/data/ruby/gdb/value.py +181 -0
- data/lib/ruby/gdb/version.rb +13 -0
- data/lib/ruby/gdb.rb +23 -0
- data/license.md +21 -0
- data/readme.md +42 -0
- metadata +69 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import gdb
|
|
2
|
+
import constants
|
|
3
|
+
import format
|
|
4
|
+
|
|
5
|
+
import rbasic
|
|
6
|
+
import rfloat
|
|
7
|
+
import rsymbol
|
|
8
|
+
import rstring
|
|
9
|
+
import rarray
|
|
10
|
+
import rhash
|
|
11
|
+
import rstruct
|
|
12
|
+
import rbignum
|
|
13
|
+
|
|
14
|
+
class RImmediate:
|
|
15
|
+
"""Wrapper for Ruby immediate values (fixnum, nil, true, false)."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, value):
|
|
18
|
+
self.value = value
|
|
19
|
+
self.val_int = int(value)
|
|
20
|
+
|
|
21
|
+
def __str__(self):
|
|
22
|
+
if self.val_int == 0:
|
|
23
|
+
return "<T_FALSE>"
|
|
24
|
+
elif self.val_int == 0x04 or self.val_int == 0x08:
|
|
25
|
+
return "<T_NIL>"
|
|
26
|
+
elif self.val_int == 0x14:
|
|
27
|
+
return "<T_TRUE>"
|
|
28
|
+
elif (self.val_int & 0x01) != 0:
|
|
29
|
+
# Fixnum - shift right to get actual value
|
|
30
|
+
return str(self.val_int >> 1)
|
|
31
|
+
else:
|
|
32
|
+
# Unknown immediate
|
|
33
|
+
return f"<Immediate:0x{self.val_int:x}>"
|
|
34
|
+
|
|
35
|
+
def print_to(self, terminal):
|
|
36
|
+
"""Print this value with formatting to the given terminal."""
|
|
37
|
+
if self.val_int == 0:
|
|
38
|
+
return terminal.print(format.metadata, '<', format.type, 'T_FALSE', format.metadata, '>', format.reset)
|
|
39
|
+
elif self.val_int == 0x04 or self.val_int == 0x08:
|
|
40
|
+
return terminal.print(format.metadata, '<', format.type, 'T_NIL', format.metadata, '>', format.reset)
|
|
41
|
+
elif self.val_int == 0x14:
|
|
42
|
+
return terminal.print(format.metadata, '<', format.type, 'T_TRUE', format.metadata, '>', format.reset)
|
|
43
|
+
elif (self.val_int & 0x01) != 0:
|
|
44
|
+
# Fixnum - shift right to get actual value
|
|
45
|
+
tag = terminal.print(format.metadata, '<', format.type, 'T_FIXNUM', format.metadata, '>', format.reset)
|
|
46
|
+
num = terminal.print(format.number, str(self.val_int >> 1), format.reset)
|
|
47
|
+
return f"{tag} {num}"
|
|
48
|
+
else:
|
|
49
|
+
# Unknown immediate
|
|
50
|
+
return f"<Immediate:0x{self.val_int:x}>"
|
|
51
|
+
|
|
52
|
+
def print_recursive(self, printer, depth):
|
|
53
|
+
"""Print this immediate value (no recursion needed)."""
|
|
54
|
+
printer.print(self)
|
|
55
|
+
|
|
56
|
+
def is_nil(value):
|
|
57
|
+
"""Check if a Ruby VALUE is nil.
|
|
58
|
+
|
|
59
|
+
Arguments:
|
|
60
|
+
value: A GDB value representing a Ruby VALUE
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
True if the value is nil (Qnil), False otherwise
|
|
64
|
+
"""
|
|
65
|
+
val_int = int(value)
|
|
66
|
+
# Qnil can be 0x04 or 0x08 depending on Ruby version
|
|
67
|
+
return val_int == 0x04 or val_int == 0x08
|
|
68
|
+
|
|
69
|
+
def is_immediate(value):
|
|
70
|
+
"""Check if a Ruby VALUE is an immediate value.
|
|
71
|
+
|
|
72
|
+
Immediate values include fixnum, symbols, nil, true, false.
|
|
73
|
+
They are encoded directly in the VALUE, not as heap objects.
|
|
74
|
+
|
|
75
|
+
Arguments:
|
|
76
|
+
value: A GDB value representing a Ruby VALUE
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
True if the value is immediate, False if it's a heap object
|
|
80
|
+
"""
|
|
81
|
+
val_int = int(value)
|
|
82
|
+
# Check for special constants (Qfalse=0, Qnil=0x04/0x08, Qtrue=0x14)
|
|
83
|
+
# or immediate values (fixnum, symbol, flonum) which have low bits set
|
|
84
|
+
return val_int == 0 or (val_int & 0x03) != 0
|
|
85
|
+
|
|
86
|
+
def is_object(value):
|
|
87
|
+
"""Check if a Ruby VALUE is a heap object (not immediate).
|
|
88
|
+
|
|
89
|
+
Arguments:
|
|
90
|
+
value: A GDB value representing a Ruby VALUE
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
True if the value is a heap object, False if it's immediate
|
|
94
|
+
"""
|
|
95
|
+
return not is_immediate(value)
|
|
96
|
+
|
|
97
|
+
def is_exception(value):
|
|
98
|
+
"""Check if a Ruby VALUE is an exception object.
|
|
99
|
+
|
|
100
|
+
Arguments:
|
|
101
|
+
value: A GDB value representing a Ruby VALUE
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
True if the value appears to be an exception object, False otherwise
|
|
105
|
+
"""
|
|
106
|
+
if not is_object(value):
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
# Check if it's a T_OBJECT or T_DATA (exceptions can be either)
|
|
111
|
+
basic = value.cast(constants.get_type("struct RBasic").pointer())
|
|
112
|
+
flags = int(basic.dereference()['flags'])
|
|
113
|
+
type_flag = flags & constants.get("RUBY_T_MASK")
|
|
114
|
+
|
|
115
|
+
# Exceptions are typically T_OBJECT, but could also be T_DATA
|
|
116
|
+
# We can't reliably determine if it's an exception without checking the class hierarchy
|
|
117
|
+
# So we just check if it's an object type that could be an exception
|
|
118
|
+
t_object = constants.get("RUBY_T_OBJECT")
|
|
119
|
+
t_data = constants.get("RUBY_T_DATA")
|
|
120
|
+
|
|
121
|
+
return type_flag == t_object or type_flag == t_data
|
|
122
|
+
except Exception:
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
def interpret(value):
|
|
126
|
+
"""Interpret a Ruby VALUE and return the appropriate typed object.
|
|
127
|
+
|
|
128
|
+
This is a factory function that examines the value and returns the
|
|
129
|
+
most specific type wrapper available (RString, RArray, RHash, etc.),
|
|
130
|
+
or RBasic as a fallback for unhandled types.
|
|
131
|
+
|
|
132
|
+
For immediate values (fixnum, flonum, symbol, nil, true, false), it returns
|
|
133
|
+
the appropriate wrapper (RImmediate, RFloat, RSymbol).
|
|
134
|
+
|
|
135
|
+
Arguments:
|
|
136
|
+
value: A GDB value representing a Ruby VALUE
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
An instance of the appropriate type class (never None)
|
|
140
|
+
"""
|
|
141
|
+
val_int = int(value)
|
|
142
|
+
|
|
143
|
+
# Check for immediate flonum (must be before fixnum check)
|
|
144
|
+
if rfloat.is_flonum(value):
|
|
145
|
+
return rfloat.RFloat(value)
|
|
146
|
+
|
|
147
|
+
# Check if it's a symbol (immediate or heap)
|
|
148
|
+
if rsymbol.is_symbol(value):
|
|
149
|
+
return rsymbol.RSymbol(value)
|
|
150
|
+
|
|
151
|
+
# Handle special constants and fixnum (anything with low bits set)
|
|
152
|
+
if val_int == 0 or val_int == 0x04 or val_int == 0x08 or val_int == 0x14 or (val_int & 0x01) != 0:
|
|
153
|
+
return RImmediate(value)
|
|
154
|
+
|
|
155
|
+
# It's a heap object, examine its type
|
|
156
|
+
try:
|
|
157
|
+
basic = value.cast(constants.get_type("struct RBasic").pointer())
|
|
158
|
+
flags = int(basic.dereference()['flags'])
|
|
159
|
+
type_flag = flags & constants.get("RUBY_T_MASK")
|
|
160
|
+
|
|
161
|
+
# Map type flags to their corresponding factory functions
|
|
162
|
+
if type_flag == constants.get("RUBY_T_STRING"):
|
|
163
|
+
return rstring.RString(value)
|
|
164
|
+
elif type_flag == constants.get("RUBY_T_ARRAY"):
|
|
165
|
+
return rarray.RArray(value)
|
|
166
|
+
elif type_flag == constants.get("RUBY_T_HASH"):
|
|
167
|
+
return rhash.RHash(value)
|
|
168
|
+
elif type_flag == constants.get("RUBY_T_STRUCT"):
|
|
169
|
+
return rstruct.RStruct(value)
|
|
170
|
+
elif type_flag == constants.get("RUBY_T_SYMBOL"):
|
|
171
|
+
return rsymbol.RSymbol(value)
|
|
172
|
+
elif type_flag == constants.get("RUBY_T_FLOAT"):
|
|
173
|
+
return rfloat.RFloat(value)
|
|
174
|
+
elif type_flag == constants.get("RUBY_T_BIGNUM"):
|
|
175
|
+
return rbignum.RBignum(value)
|
|
176
|
+
else:
|
|
177
|
+
# Unknown type - return generic RBasic
|
|
178
|
+
return rbasic.RBasic(value)
|
|
179
|
+
except Exception:
|
|
180
|
+
# If we can't examine it, return a generic wrapper
|
|
181
|
+
return rbasic.RBasic(value)
|
data/lib/ruby/gdb.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require_relative "gdb/version"
|
|
7
|
+
|
|
8
|
+
module Ruby
|
|
9
|
+
module GDB
|
|
10
|
+
# Path to the GDB extension scripts.
|
|
11
|
+
def self.data_path
|
|
12
|
+
File.expand_path("../../data", __dir__)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Path to the init.py script.
|
|
16
|
+
def self.init_script_path
|
|
17
|
+
File.join(data_path, "ruby", "gdb", "init.py")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
data/license.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright, 2025, by Samuel Williams.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/readme.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Ruby::GDB
|
|
2
|
+
|
|
3
|
+
Ruby debugging extensions for GDB, providing commands for inspecting Ruby objects, fibers, and VM internals.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/socketry/ruby-gdb/actions?workflow=Test)
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
Please see the [project documentation](https://socketry.github.io/ruby-gdb/) for more details.
|
|
10
|
+
|
|
11
|
+
- [Getting Started](https://socketry.github.io/ruby-gdb/guides/getting-started/index) - This guide explains how to install and use Ruby GDB extensions for debugging Ruby programs and core dumps.
|
|
12
|
+
|
|
13
|
+
- [Object Inspection](https://socketry.github.io/ruby-gdb/guides/object-inspection/index) - This guide explains how to use `rb-object-print` to inspect Ruby objects, hashes, arrays, and structs in GDB.
|
|
14
|
+
|
|
15
|
+
- [Stack Inspection](https://socketry.github.io/ruby-gdb/guides/stack-inspection/index) - This guide explains how to inspect both Ruby VM stacks and native C stacks when debugging Ruby programs.
|
|
16
|
+
|
|
17
|
+
- [Fiber Debugging](https://socketry.github.io/ruby-gdb/guides/fiber-debugging/index) - This guide explains how to debug Ruby fibers using GDB, including inspecting fiber state, backtraces, and switching between fiber contexts.
|
|
18
|
+
|
|
19
|
+
- [Heap Debugging](https://socketry.github.io/ruby-gdb/guides/heap-debugging/index) - This guide explains how to navigate Ruby's heap to find objects, diagnose memory issues, and understand object relationships.
|
|
20
|
+
|
|
21
|
+
## See Also
|
|
22
|
+
|
|
23
|
+
- [GDB Python API Documentation](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Python-API.html)
|
|
24
|
+
- [Ruby VM Internals](https://docs.ruby-lang.org/en/master/extension_rdoc.html)
|
|
25
|
+
|
|
26
|
+
## Contributing
|
|
27
|
+
|
|
28
|
+
We welcome contributions to this project.
|
|
29
|
+
|
|
30
|
+
1. Fork it.
|
|
31
|
+
2. Create your feature branch (`git checkout -b my-new-feature`).
|
|
32
|
+
3. Commit your changes (`git commit -am 'Add some feature'`).
|
|
33
|
+
4. Push to the branch (`git push origin my-new-feature`).
|
|
34
|
+
5. Create new Pull Request.
|
|
35
|
+
|
|
36
|
+
### Developer Certificate of Origin
|
|
37
|
+
|
|
38
|
+
In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
|
|
39
|
+
|
|
40
|
+
### Community Guidelines
|
|
41
|
+
|
|
42
|
+
This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
|
metadata
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ruby-gdb
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Samuel Williams
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
executables: []
|
|
13
|
+
extensions: []
|
|
14
|
+
extra_rdoc_files: []
|
|
15
|
+
files:
|
|
16
|
+
- bake/ruby/gdb.rb
|
|
17
|
+
- context/fiber-debugging.md
|
|
18
|
+
- context/getting-started.md
|
|
19
|
+
- context/heap-debugging.md
|
|
20
|
+
- context/index.yaml
|
|
21
|
+
- context/object-inspection.md
|
|
22
|
+
- context/stack-inspection.md
|
|
23
|
+
- data/ruby/gdb/command.py
|
|
24
|
+
- data/ruby/gdb/constants.py
|
|
25
|
+
- data/ruby/gdb/fiber.py
|
|
26
|
+
- data/ruby/gdb/format.py
|
|
27
|
+
- data/ruby/gdb/heap.py
|
|
28
|
+
- data/ruby/gdb/init.py
|
|
29
|
+
- data/ruby/gdb/object.py
|
|
30
|
+
- data/ruby/gdb/rarray.py
|
|
31
|
+
- data/ruby/gdb/rbasic.py
|
|
32
|
+
- data/ruby/gdb/rbignum.py
|
|
33
|
+
- data/ruby/gdb/rclass.py
|
|
34
|
+
- data/ruby/gdb/rexception.py
|
|
35
|
+
- data/ruby/gdb/rfloat.py
|
|
36
|
+
- data/ruby/gdb/rhash.py
|
|
37
|
+
- data/ruby/gdb/rstring.py
|
|
38
|
+
- data/ruby/gdb/rstruct.py
|
|
39
|
+
- data/ruby/gdb/rsymbol.py
|
|
40
|
+
- data/ruby/gdb/stack.py
|
|
41
|
+
- data/ruby/gdb/value.py
|
|
42
|
+
- lib/ruby/gdb.rb
|
|
43
|
+
- lib/ruby/gdb/version.rb
|
|
44
|
+
- license.md
|
|
45
|
+
- readme.md
|
|
46
|
+
homepage: https://github.com/socketry/ruby-gdb
|
|
47
|
+
licenses:
|
|
48
|
+
- MIT
|
|
49
|
+
metadata:
|
|
50
|
+
documentation_uri: https://socketry.github.io/ruby-gdb/
|
|
51
|
+
source_code_uri: https://github.com/socketry/ruby-gdb
|
|
52
|
+
rdoc_options: []
|
|
53
|
+
require_paths:
|
|
54
|
+
- lib
|
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '3.2'
|
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
61
|
+
requirements:
|
|
62
|
+
- - ">="
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
version: '0'
|
|
65
|
+
requirements: []
|
|
66
|
+
rubygems_version: 3.6.9
|
|
67
|
+
specification_version: 4
|
|
68
|
+
summary: Ruby debugging extensions for GDB
|
|
69
|
+
test_files: []
|