@@ -13,6 +13,22 @@ options = OpenStruct.new(
13
13
'gems_to_include' => [ ]
14
14
)
15
15
16
+ module DebugPrinter
17
+
18
+ class << self
19
+ attr_accessor :cli_debug
20
+
21
+ def print_debug ( msg )
22
+ if DebugPrinter . cli_debug
23
+ $stderr. puts msg
24
+ end
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ DebugPrinter . cli_debug = ARGV . include? '--debug'
31
+
16
32
opts = OptionParser . new do |opts |
17
33
# TODO need some banner
18
34
opts . banner = <<EOB
@@ -75,17 +91,31 @@ class NativeDebugger
75
91
if debase_path . size == 0
76
92
raise 'No debase gem found.'
77
93
end
78
- @path_to_attach = debase_path [ 0 ] + '/attach.so'
94
+ @path_to_attach = find_attach_lib ( debase_path [ 0 ] )
79
95
80
96
@gems_to_include = '["' + gems_to_include * '", "' + '"]'
81
97
@debugger_loader_path = debugger_loader_path
82
98
@argv = argv
83
99
100
+ @eval_string = "rb_eval_string_protect(\" require '#{ @debugger_loader_path } '; load_debugger(#{ @gems_to_include . gsub ( "\" " , "'" ) } , #{ @argv . gsub ( "\" " , "'" ) } )\" , (int *)0)"
101
+
84
102
launch_string = "#{ self } #{ executable } #{ flags } "
85
103
@pipe = IO . popen ( launch_string , 'r+' )
86
104
$stdout. puts "executed '#{ launch_string } '"
87
105
end
88
106
107
+ def find_attach_lib ( debase_path )
108
+ attach_lib = debase_path + '/attach'
109
+ known_extensions = %w( .so .bundle .dll )
110
+ known_extensions . each do |ext |
111
+ if File . file? ( attach_lib + ext )
112
+ return attach_lib + ext
113
+ end
114
+ end
115
+
116
+ raise 'Could not find attach library'
117
+ end
118
+
89
119
def attach_to_process
90
120
execute "attach #{ @pid } "
91
121
end
@@ -101,15 +131,17 @@ class NativeDebugger
101
131
102
132
def get_response
103
133
# we need this hack to understand that debugger gave us all output from last executed command
104
- @pipe . puts "print \" #{ @delimiter } \" "
134
+ print_delimiter
105
135
106
136
content = ''
107
137
loop do
108
138
line = @pipe . readline
139
+ break if check_delimiter ( line )
140
+ DebugPrinter . print_debug ( 'respond line: ' + line )
109
141
next if line =~ /\( lldb\) / # lldb repeats your input to its output
110
- break if line =~ /\$ \d +\s =\s "#{ @delimiter } "/
111
142
content += line
112
143
end
144
+
113
145
content
114
146
end
115
147
@@ -121,6 +153,14 @@ class NativeDebugger
121
153
122
154
end
123
155
156
+ def print_delimiter
157
+
158
+ end
159
+
160
+ def check_delimiter ( line )
161
+
162
+ end
163
+
124
164
def switch_to_thread
125
165
126
166
end
@@ -150,7 +190,7 @@ class NativeDebugger
150
190
end
151
191
152
192
def load_debugger
153
- execute "call rb_eval_string_protect( \" require ' #{ @debugger_loader_path } '; load_debugger( #{ @gems_to_include . gsub ( " \" " , "'" ) } , #{ @argv . gsub ( " \" " , "'" ) } ) \" , (int *)0)"
193
+
154
194
end
155
195
156
196
def exit
@@ -179,7 +219,6 @@ class LLDB < NativeDebugger
179
219
info_threads = ( execute 'thread list' ) . split ( "\n " )
180
220
info_threads . each do |thread_info |
181
221
next unless thread_info =~ /[\s *]*thread\s #\d +.*/
182
- $stdout. puts "thread_info: #{ thread_info } "
183
222
is_main = thread_info [ 0 ] == '*'
184
223
thread_num = thread_info . sub ( /[\s *]*thread\s #/ , '' ) . sub ( /:\s .*$/ , '' ) . to_i
185
224
thread = ProcessThread . new ( thread_num , is_main , thread_info , self )
@@ -203,10 +242,22 @@ class LLDB < NativeDebugger
203
242
def call_start_attach
204
243
super ( )
205
244
execute "expr (void *) dlopen(\" #{ @path_to_attach } \" , 2)"
206
- execute 'call start_attach()'
245
+ execute 'expr (int) start_attach()'
207
246
set_tbreak ( @tbreak )
208
247
end
209
248
249
+ def print_delimiter
250
+ @pipe . puts "script print \" #{ @delimiter } \" "
251
+ end
252
+
253
+ def check_delimiter ( line )
254
+ line =~ /#{ @delimiter } $/
255
+ end
256
+
257
+ def load_debugger
258
+ execute "expr (VALUE) #{ @eval_string } "
259
+ end
260
+
210
261
def to_s
211
262
'lldb'
212
263
end
@@ -257,6 +308,18 @@ class GDB < NativeDebugger
257
308
set_tbreak ( @tbreak )
258
309
end
259
310
311
+ def print_delimiter
312
+ @pipe . puts "print \" #{ @delimiter } \" "
313
+ end
314
+
315
+ def check_delimiter ( line )
316
+ line =~ /\$ \d +\s =\s "#{ @delimiter } "/
317
+ end
318
+
319
+ def load_debugger
320
+ execute "call #{ @eval_string } "
321
+ end
322
+
260
323
def to_s
261
324
'gdb'
262
325
end
@@ -317,15 +380,21 @@ class ProcessThread
317
380
end
318
381
319
382
def command_exists ( command )
383
+ checking_command = "checking command #{ command } for existence\n "
320
384
`command -v #{ command } >/dev/null 2>&1 || { exit 1; }`
385
+ if $?. exitstatus != 0
386
+ DebugPrinter . print_debug ( "#{ checking_command } command does not exist." )
387
+ else
388
+ DebugPrinter . print_debug ( "#{ checking_command } command does exist." )
389
+ end
321
390
$?. exitstatus == 0
322
391
end
323
392
324
393
def choose_debugger ( ruby_path , pid , gems_to_include , debugger_loader_path , argv )
325
- if command_exists ( 'gdb' )
326
- debugger = GDB . new ( ruby_path , pid , '-nh -nx' , gems_to_include , debugger_loader_path , argv )
327
- elsif command_exists ( 'lldb' )
394
+ if command_exists ( 'lldb' )
328
395
debugger = LLDB . new ( ruby_path , pid , '--no-lldbinit' , gems_to_include , debugger_loader_path , argv )
396
+ elsif command_exists ( 'gdb' )
397
+ debugger = GDB . new ( ruby_path , pid , '-nh -nx' , gems_to_include , debugger_loader_path , argv )
329
398
else
330
399
raise 'Neither gdb nor lldb was found. Aborting.'
331
400
end
@@ -348,6 +417,7 @@ debugger.attach_to_process
348
417
debugger . set_flags
349
418
350
419
if options . uid
420
+ DebugPrinter . print_debug ( "changing current uid from #{ Process . uid } to #{ options . uid } " )
351
421
Process ::Sys . setuid ( options . uid . to_i )
352
422
end
353
423
0 commit comments