lldb 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.
data/lib/lldb/value.rb ADDED
@@ -0,0 +1,334 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ module LLDB
6
+ class Value
7
+ include Enumerable #[Value]
8
+
9
+ # @rbs return: Frame | Value | Target
10
+ attr_reader :parent
11
+
12
+ # @rbs ptr: FFI::Pointer
13
+ # @rbs parent: Frame | Value | Target
14
+ # @rbs return: void
15
+ def initialize(ptr, parent:)
16
+ @ptr = ptr # : FFI::Pointer
17
+ @parent = parent
18
+ ObjectSpace.define_finalizer(self, self.class.release(@ptr))
19
+ end
20
+
21
+ # @rbs ptr: FFI::Pointer
22
+ # @rbs return: ^(Integer) -> void
23
+ def self.release(ptr)
24
+ ->(_id) { FFIBindings.lldb_value_destroy(ptr) unless ptr.null? }
25
+ end
26
+
27
+ # @rbs return: bool
28
+ def valid?
29
+ !@ptr.null? && FFIBindings.lldb_value_is_valid(@ptr) != 0
30
+ end
31
+
32
+ # @rbs return: String?
33
+ def name
34
+ return nil unless valid?
35
+
36
+ FFIBindings.lldb_value_get_name(@ptr)
37
+ end
38
+
39
+ # @rbs return: String?
40
+ def value
41
+ return nil unless valid?
42
+
43
+ FFIBindings.lldb_value_get_value(@ptr)
44
+ end
45
+
46
+ # @rbs return: String?
47
+ def summary
48
+ return nil unless valid?
49
+
50
+ FFIBindings.lldb_value_get_summary(@ptr)
51
+ end
52
+
53
+ # @rbs return: String?
54
+ def type_name
55
+ return nil unless valid?
56
+
57
+ FFIBindings.lldb_value_get_type_name(@ptr)
58
+ end
59
+
60
+ # @rbs return: Integer
61
+ def num_children
62
+ return 0 unless valid?
63
+
64
+ FFIBindings.lldb_value_get_num_children(@ptr)
65
+ end
66
+
67
+ # @rbs index: Integer
68
+ # @rbs return: Value?
69
+ def child_at_index(index)
70
+ raise InvalidObjectError, 'Value is not valid' unless valid?
71
+
72
+ child_ptr = FFIBindings.lldb_value_get_child_at_index(@ptr, index)
73
+ return nil if child_ptr.nil? || child_ptr.null?
74
+
75
+ Value.new(child_ptr, parent: self)
76
+ end
77
+
78
+ # @rbs name: String
79
+ # @rbs return: Value?
80
+ def child_member(name)
81
+ raise InvalidObjectError, 'Value is not valid' unless valid?
82
+
83
+ child_ptr = FFIBindings.lldb_value_get_child_member_with_name(@ptr, name)
84
+ return nil if child_ptr.nil? || child_ptr.null?
85
+
86
+ Value.new(child_ptr, parent: self)
87
+ end
88
+
89
+ alias [] child_member
90
+
91
+ # @rbs return: Array[Value]
92
+ def children
93
+ (0...num_children).map { |i| child_at_index(i) }.compact
94
+ end
95
+
96
+ # @rbs &block: (Value) -> void
97
+ # @rbs return: Enumerator[Value, void] | void
98
+ def each(&block)
99
+ return enum_for(:each) unless block_given?
100
+
101
+ children.each(&block)
102
+ end
103
+
104
+ # @rbs return: Integer
105
+ def value_as_signed
106
+ return 0 unless valid?
107
+
108
+ FFIBindings.lldb_value_get_value_as_signed(@ptr)
109
+ end
110
+
111
+ alias to_i value_as_signed
112
+
113
+ # @rbs return: Integer
114
+ def value_as_unsigned
115
+ return 0 unless valid?
116
+
117
+ FFIBindings.lldb_value_get_value_as_unsigned(@ptr)
118
+ end
119
+
120
+ # @rbs return: Integer
121
+ def byte_size
122
+ return 0 unless valid?
123
+
124
+ FFIBindings.lldb_value_get_byte_size(@ptr)
125
+ end
126
+
127
+ # @rbs return: bool
128
+ def might_have_children?
129
+ return false unless valid?
130
+
131
+ FFIBindings.lldb_value_might_have_children(@ptr) != 0
132
+ end
133
+
134
+ # @rbs return: Error
135
+ def error
136
+ raise InvalidObjectError, 'Value is not valid' unless valid?
137
+
138
+ error = Error.new
139
+ FFIBindings.lldb_value_get_error(@ptr, error.to_ptr)
140
+ error
141
+ end
142
+
143
+ # @rbs return: bool
144
+ def has_error?
145
+ err = error
146
+ err.fail?
147
+ end
148
+
149
+ # @rbs return: Value?
150
+ def dereference
151
+ raise InvalidObjectError, 'Value is not valid' unless valid?
152
+
153
+ deref_ptr = FFIBindings.lldb_value_dereference(@ptr)
154
+ return nil if deref_ptr.nil? || deref_ptr.null?
155
+
156
+ Value.new(deref_ptr, parent: self)
157
+ end
158
+
159
+ # @rbs return: Value?
160
+ def address_of
161
+ raise InvalidObjectError, 'Value is not valid' unless valid?
162
+
163
+ addr_ptr = FFIBindings.lldb_value_address_of(@ptr)
164
+ return nil if addr_ptr.nil? || addr_ptr.null?
165
+
166
+ Value.new(addr_ptr, parent: self)
167
+ end
168
+
169
+ # @rbs return: String
170
+ def to_s
171
+ if summary
172
+ "#{name} = #{summary}"
173
+ elsif value
174
+ "#{name} = #{value}"
175
+ else
176
+ "#{name} (#{type_name})"
177
+ end
178
+ end
179
+
180
+ # @rbs return: String
181
+ def inspect
182
+ "#<LLDB::Value name=#{name.inspect} type=#{type_name.inspect} value=#{value.inspect}>"
183
+ end
184
+
185
+ # @rbs return: Type?
186
+ def type
187
+ raise InvalidObjectError, 'Value is not valid' unless valid?
188
+
189
+ type_ptr = FFIBindings.lldb_value_get_type(@ptr)
190
+ return nil if type_ptr.nil? || type_ptr.null?
191
+
192
+ Type.new(type_ptr)
193
+ end
194
+
195
+ # @rbs target_type: Type
196
+ # @rbs return: Value?
197
+ def cast(target_type)
198
+ raise InvalidObjectError, 'Value is not valid' unless valid?
199
+
200
+ cast_ptr = FFIBindings.lldb_value_cast(@ptr, target_type.to_ptr)
201
+ return nil if cast_ptr.nil? || cast_ptr.null?
202
+
203
+ Value.new(cast_ptr, parent: self)
204
+ end
205
+
206
+ # @rbs return: Integer
207
+ def load_address
208
+ return 0 unless valid?
209
+
210
+ FFIBindings.lldb_value_get_load_address(@ptr)
211
+ end
212
+
213
+ # @rbs return: Integer
214
+ def value_type
215
+ return ValueType::INVALID unless valid?
216
+
217
+ FFIBindings.lldb_value_get_value_type(@ptr)
218
+ end
219
+
220
+ # @rbs return: String
221
+ def value_type_name
222
+ ValueType.name(value_type)
223
+ end
224
+
225
+ # @rbs str: String
226
+ # @rbs return: bool
227
+ def set_value_from_cstring(str)
228
+ raise InvalidObjectError, 'Value is not valid' unless valid?
229
+
230
+ error = Error.new
231
+ result = FFIBindings.lldb_value_set_value_from_cstring(@ptr, str, error.to_ptr)
232
+ error.raise_if_error!
233
+ result != 0
234
+ end
235
+
236
+ # @rbs name: String
237
+ # @rbs value_type: Type
238
+ # @rbs offset: Integer
239
+ # @rbs return: Value?
240
+ def create_child_at_offset(name, value_type, offset)
241
+ raise InvalidObjectError, 'Value is not valid' unless valid?
242
+
243
+ child_ptr = FFIBindings.lldb_value_create_child_at_offset(@ptr, name, value_type.to_ptr, offset)
244
+ return nil if child_ptr.nil? || child_ptr.null?
245
+
246
+ Value.new(child_ptr, parent: self)
247
+ end
248
+
249
+ # @rbs name: String
250
+ # @rbs address: Integer
251
+ # @rbs value_type: Type
252
+ # @rbs return: Value?
253
+ def create_value_from_address(name, address, value_type)
254
+ raise InvalidObjectError, 'Value is not valid' unless valid?
255
+
256
+ value_ptr = FFIBindings.lldb_value_create_value_from_address(@ptr, name, address, value_type.to_ptr)
257
+ return nil if value_ptr.nil? || value_ptr.null?
258
+
259
+ Value.new(value_ptr, parent: self)
260
+ end
261
+
262
+ # @rbs name: String
263
+ # @rbs expression: String
264
+ # @rbs return: Value?
265
+ def create_value_from_expression(name, expression)
266
+ raise InvalidObjectError, 'Value is not valid' unless valid?
267
+
268
+ value_ptr = FFIBindings.lldb_value_create_value_from_expression(@ptr, name, expression)
269
+ return nil if value_ptr.nil? || value_ptr.null?
270
+
271
+ Value.new(value_ptr, parent: self)
272
+ end
273
+
274
+ # @rbs resolve: bool
275
+ # @rbs read: bool
276
+ # @rbs write: bool
277
+ # @rbs return: Watchpoint?
278
+ def watch(resolve: true, read: false, write: true)
279
+ raise InvalidObjectError, 'Value is not valid' unless valid?
280
+
281
+ error = Error.new
282
+ wp_ptr = FFIBindings.lldb_value_watch(@ptr, resolve ? 1 : 0, read ? 1 : 0, write ? 1 : 0, error.to_ptr)
283
+
284
+ error.raise_if_error!
285
+ return nil if wp_ptr.nil? || wp_ptr.null?
286
+
287
+ target = get_target_from_parent
288
+ Watchpoint.new(wp_ptr, target: target)
289
+ end
290
+
291
+ # @rbs return: String?
292
+ def expression_path
293
+ return nil unless valid?
294
+
295
+ FFIBindings.lldb_value_get_expression_path(@ptr)
296
+ end
297
+
298
+ # @rbs return: bool
299
+ def pointer_type?
300
+ return false unless valid?
301
+
302
+ FFIBindings.lldb_value_is_pointer_type(@ptr) != 0
303
+ end
304
+
305
+ # @rbs return: Value?
306
+ def non_synthetic_value
307
+ raise InvalidObjectError, 'Value is not valid' unless valid?
308
+
309
+ value_ptr = FFIBindings.lldb_value_get_non_synthetic_value(@ptr)
310
+ return nil if value_ptr.nil? || value_ptr.null?
311
+
312
+ Value.new(value_ptr, parent: self)
313
+ end
314
+
315
+ # @rbs return: FFI::Pointer
316
+ def to_ptr
317
+ @ptr
318
+ end
319
+
320
+ private
321
+
322
+ # @rbs return: Target
323
+ def get_target_from_parent
324
+ current = @parent
325
+ while current
326
+ return current.target if current.respond_to?(:target)
327
+ return current if current.is_a?(Target)
328
+
329
+ current = current.respond_to?(:parent) ? current.parent : nil
330
+ end
331
+ raise LLDBError, 'Could not find target from value hierarchy'
332
+ end
333
+ end
334
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ module LLDB
6
+ class ValueList
7
+ include Enumerable #[Value]
8
+
9
+ # @rbs return: Frame | Value
10
+ attr_reader :parent
11
+
12
+ # @rbs ptr: FFI::Pointer
13
+ # @rbs parent: Frame | Value
14
+ # @rbs return: void
15
+ def initialize(ptr, parent:)
16
+ @ptr = ptr # : FFI::Pointer
17
+ @parent = parent
18
+ ObjectSpace.define_finalizer(self, self.class.release(@ptr))
19
+ end
20
+
21
+ # @rbs ptr: FFI::Pointer
22
+ # @rbs return: ^(Integer) -> void
23
+ def self.release(ptr)
24
+ ->(_id) { FFIBindings.lldb_value_list_destroy(ptr) unless ptr.null? }
25
+ end
26
+
27
+ # @rbs return: bool
28
+ def valid?
29
+ !@ptr.null? && FFIBindings.lldb_value_list_is_valid(@ptr) != 0
30
+ end
31
+
32
+ # @rbs return: Integer
33
+ def size
34
+ return 0 unless valid?
35
+
36
+ FFIBindings.lldb_value_list_get_size(@ptr)
37
+ end
38
+
39
+ alias length size
40
+
41
+ # @rbs index: Integer
42
+ # @rbs return: Value?
43
+ def value_at_index(index)
44
+ raise InvalidObjectError, 'ValueList is not valid' unless valid?
45
+
46
+ value_ptr = FFIBindings.lldb_value_list_get_value_at_index(@ptr, index)
47
+ return nil if value_ptr.nil? || value_ptr.null?
48
+
49
+ Value.new(value_ptr, parent: @parent)
50
+ end
51
+
52
+ alias [] value_at_index
53
+
54
+ # @rbs name: String
55
+ # @rbs return: Value?
56
+ def first_value_by_name(name)
57
+ raise InvalidObjectError, 'ValueList is not valid' unless valid?
58
+
59
+ value_ptr = FFIBindings.lldb_value_list_get_first_value_by_name(@ptr, name)
60
+ return nil if value_ptr.nil? || value_ptr.null?
61
+
62
+ Value.new(value_ptr, parent: @parent)
63
+ end
64
+
65
+ # @rbs return: Array[Value]
66
+ def to_a
67
+ (0...size).map { |i| value_at_index(i) }.compact
68
+ end
69
+
70
+ # @rbs &block: (Value) -> void
71
+ # @rbs return: Enumerator[Value, void] | void
72
+ def each(&block)
73
+ return enum_for(:each) unless block_given?
74
+
75
+ to_a.each(&block)
76
+ end
77
+
78
+ # @rbs return: bool
79
+ def empty?
80
+ size.zero?
81
+ end
82
+
83
+ # @rbs return: FFI::Pointer
84
+ def to_ptr
85
+ @ptr
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ module LLDB
6
+ VERSION = '0.1.0' # : String
7
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ module LLDB
6
+ class Watchpoint
7
+ # @rbs return: Target
8
+ attr_reader :target
9
+
10
+ # @rbs ptr: FFI::Pointer
11
+ # @rbs target: Target
12
+ # @rbs return: void
13
+ def initialize(ptr, target:)
14
+ @ptr = ptr # : FFI::Pointer
15
+ @target = target
16
+ ObjectSpace.define_finalizer(self, self.class.release(@ptr))
17
+ end
18
+
19
+ # @rbs ptr: FFI::Pointer
20
+ # @rbs return: ^(Integer) -> void
21
+ def self.release(ptr)
22
+ ->(_id) { FFIBindings.lldb_watchpoint_destroy(ptr) unless ptr.null? }
23
+ end
24
+
25
+ # @rbs return: bool
26
+ def valid?
27
+ !@ptr.null? && FFIBindings.lldb_watchpoint_is_valid(@ptr) != 0
28
+ end
29
+
30
+ # @rbs return: Integer
31
+ def id
32
+ return -1 unless valid?
33
+
34
+ FFIBindings.lldb_watchpoint_get_id(@ptr)
35
+ end
36
+
37
+ # @rbs return: bool
38
+ def enabled?
39
+ return false unless valid?
40
+
41
+ FFIBindings.lldb_watchpoint_is_enabled(@ptr) != 0
42
+ end
43
+
44
+ # @rbs value: bool
45
+ # @rbs return: void
46
+ def enabled=(value)
47
+ raise InvalidObjectError, 'Watchpoint is not valid' unless valid?
48
+
49
+ FFIBindings.lldb_watchpoint_set_enabled(@ptr, value ? 1 : 0)
50
+ end
51
+
52
+ # @rbs return: void
53
+ def enable
54
+ self.enabled = true
55
+ end
56
+
57
+ # @rbs return: void
58
+ def disable
59
+ self.enabled = false
60
+ end
61
+
62
+ # @rbs return: Integer
63
+ def hit_count
64
+ return 0 unless valid?
65
+
66
+ FFIBindings.lldb_watchpoint_get_hit_count(@ptr)
67
+ end
68
+
69
+ # @rbs return: Integer
70
+ def ignore_count
71
+ return 0 unless valid?
72
+
73
+ FFIBindings.lldb_watchpoint_get_ignore_count(@ptr)
74
+ end
75
+
76
+ # @rbs count: Integer
77
+ # @rbs return: void
78
+ def ignore_count=(count)
79
+ raise InvalidObjectError, 'Watchpoint is not valid' unless valid?
80
+
81
+ FFIBindings.lldb_watchpoint_set_ignore_count(@ptr, count)
82
+ end
83
+
84
+ # @rbs return: String?
85
+ def condition
86
+ return nil unless valid?
87
+
88
+ cond = FFIBindings.lldb_watchpoint_get_condition(@ptr)
89
+ cond.nil? || cond.empty? ? nil : cond
90
+ end
91
+
92
+ # @rbs expr: String?
93
+ # @rbs return: void
94
+ def condition=(expr)
95
+ raise InvalidObjectError, 'Watchpoint is not valid' unless valid?
96
+
97
+ FFIBindings.lldb_watchpoint_set_condition(@ptr, expr)
98
+ end
99
+
100
+ # @rbs return: Integer
101
+ def watch_address
102
+ return 0 unless valid?
103
+
104
+ FFIBindings.lldb_watchpoint_get_watch_address(@ptr)
105
+ end
106
+
107
+ # @rbs return: Integer
108
+ def watch_size
109
+ return 0 unless valid?
110
+
111
+ FFIBindings.lldb_watchpoint_get_watch_size(@ptr)
112
+ end
113
+
114
+ # @rbs return: bool
115
+ def watching_reads?
116
+ return false unless valid?
117
+
118
+ FFIBindings.lldb_watchpoint_is_watching_reads(@ptr) != 0
119
+ end
120
+
121
+ # @rbs return: bool
122
+ def watching_writes?
123
+ return false unless valid?
124
+
125
+ FFIBindings.lldb_watchpoint_is_watching_writes(@ptr) != 0
126
+ end
127
+
128
+ # @rbs return: bool
129
+ def delete
130
+ raise InvalidObjectError, 'Watchpoint is not valid' unless valid?
131
+
132
+ target.delete_watchpoint(id)
133
+ end
134
+
135
+ # @rbs return: String
136
+ def to_s
137
+ "Watchpoint #{id}: address=0x#{watch_address.to_s(16)}, size=#{watch_size}, enabled=#{enabled?}"
138
+ end
139
+
140
+ # @rbs return: FFI::Pointer
141
+ def to_ptr
142
+ @ptr
143
+ end
144
+ end
145
+ end
data/lib/lldb.rb ADDED
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ require_relative 'lldb/version'
6
+ require_relative 'lldb/ffi_bindings'
7
+ require_relative 'lldb/types'
8
+ require_relative 'lldb/error'
9
+ require_relative 'lldb/debugger'
10
+ require_relative 'lldb/target'
11
+ require_relative 'lldb/launch_info'
12
+ require_relative 'lldb/process'
13
+ require_relative 'lldb/thread'
14
+ require_relative 'lldb/frame'
15
+ require_relative 'lldb/breakpoint'
16
+ require_relative 'lldb/breakpoint_location'
17
+ require_relative 'lldb/value'
18
+ require_relative 'lldb/value_list'
19
+ require_relative 'lldb/type'
20
+ require_relative 'lldb/watchpoint'
21
+ require_relative 'lldb/module'
22
+ require_relative 'lldb/symbol_context'
23
+ require_relative 'lldb/command_return_object'
24
+ require_relative 'lldb/command_interpreter'
25
+
26
+ module LLDB
27
+ class << self
28
+ # @rbs return: void
29
+ def initialize
30
+ return if @initialized
31
+
32
+ FFIBindings.lldb_initialize
33
+ @initialized = true
34
+
35
+ at_exit { terminate }
36
+ end
37
+
38
+ # @rbs return: void
39
+ def terminate
40
+ return unless @initialized
41
+
42
+ FFIBindings.lldb_terminate
43
+ @initialized = false
44
+ end
45
+
46
+ # @rbs return: bool
47
+ def initialized?
48
+ @initialized == true
49
+ end
50
+
51
+ # @rbs return: void
52
+ def ensure_initialized!
53
+ return if initialized?
54
+
55
+ raise LLDBError, 'LLDB has not been initialized. Call LLDB.initialize first.'
56
+ end
57
+
58
+ # @rbs return: Debugger
59
+ def create_debugger
60
+ LLDB.initialize
61
+ Debugger.create
62
+ end
63
+ end
64
+ end
data/lldb.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/lldb/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lldb'
7
+ spec.version = LLDB::VERSION
8
+ spec.authors = ['Yudai Takada']
9
+ spec.email = ['t.yudai92@gmail.com']
10
+
11
+ spec.summary = 'Ruby bindings for LLDB debugger'
12
+ spec.description = 'Access LLDB debugger functionality from Ruby via FFI'
13
+ spec.homepage = 'https://github.com/ydah/lldb-ruby'
14
+ spec.licenses = ['MIT', 'Apache-2.0']
15
+ spec.required_ruby_version = '>= 3.0.0'
16
+
17
+ spec.metadata['source_code_uri'] = spec.homepage
18
+ spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
19
+ spec.metadata['rubygems_mfa_required'] = 'true'
20
+
21
+ spec.files = Dir.chdir(__dir__) do
22
+ `git ls-files -z`.split("\x0").reject do |f|
23
+ (File.expand_path(f) == __FILE__) ||
24
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile Dockerfile])
25
+ end
26
+ end
27
+
28
+ spec.files += Dir['lib/**/*.rb', 'ext/**/*']
29
+ spec.extensions = ['ext/lldb/extconf.rb']
30
+ spec.require_paths = ['lib']
31
+
32
+ spec.add_dependency 'ffi', '~> 1.15'
33
+ end