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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE-APACHE +190 -0
- data/LICENSE-MIT +21 -0
- data/README.md +240 -0
- data/Rakefile +80 -0
- data/Steepfile +11 -0
- data/examples/basic_debug.rb +111 -0
- data/examples/breakpoints.rb +72 -0
- data/examples/expression_eval.rb +84 -0
- data/examples/test_program.c +14 -0
- data/ext/lldb/Makefile +24 -0
- data/ext/lldb/extconf.rb +160 -0
- data/ext/lldb/liblldb_wrapper.dylib +0 -0
- data/ext/lldb/lldb_wrapper.cpp +2051 -0
- data/ext/lldb/lldb_wrapper.h +424 -0
- data/ext/lldb/lldb_wrapper.o +0 -0
- data/ext/lldb/mkmf.log +24 -0
- data/lib/lldb/breakpoint.rb +233 -0
- data/lib/lldb/breakpoint_location.rb +117 -0
- data/lib/lldb/command_interpreter.rb +62 -0
- data/lib/lldb/command_return_object.rb +71 -0
- data/lib/lldb/debugger.rb +179 -0
- data/lib/lldb/error.rb +70 -0
- data/lib/lldb/ffi_bindings.rb +394 -0
- data/lib/lldb/frame.rb +226 -0
- data/lib/lldb/launch_info.rb +85 -0
- data/lib/lldb/module.rb +61 -0
- data/lib/lldb/process.rb +317 -0
- data/lib/lldb/symbol_context.rb +52 -0
- data/lib/lldb/target.rb +427 -0
- data/lib/lldb/thread.rb +226 -0
- data/lib/lldb/type.rb +215 -0
- data/lib/lldb/types.rb +190 -0
- data/lib/lldb/value.rb +334 -0
- data/lib/lldb/value_list.rb +88 -0
- data/lib/lldb/version.rb +7 -0
- data/lib/lldb/watchpoint.rb +145 -0
- data/lib/lldb.rb +64 -0
- data/lldb.gemspec +33 -0
- data/rbs_collection.lock.yaml +220 -0
- data/rbs_collection.yaml +19 -0
- data/sig/lldb/ffi_bindings.rbs +312 -0
- metadata +102 -0
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
|
data/lib/lldb/version.rb
ADDED
|
@@ -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
|