Skip to content

Commit f56827b

Browse files
authored
Merge pull request #83 from equivalence1/ruby-attach-to-process
Choose lldb over gdb and find proper extension of library
2 parents a193e6b + f3955ca commit f56827b

File tree

1 file changed

+79
-9
lines changed

1 file changed

+79
-9
lines changed

bin/gdb_wrapper

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,22 @@ options = OpenStruct.new(
1313
'gems_to_include' => []
1414
)
1515

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+
1632
opts = OptionParser.new do |opts|
1733
# TODO need some banner
1834
opts.banner = <<EOB
@@ -75,17 +91,31 @@ class NativeDebugger
7591
if debase_path.size == 0
7692
raise 'No debase gem found.'
7793
end
78-
@path_to_attach = debase_path[0] + '/attach.so'
94+
@path_to_attach = find_attach_lib(debase_path[0])
7995

8096
@gems_to_include = '["' + gems_to_include * '", "' + '"]'
8197
@debugger_loader_path = debugger_loader_path
8298
@argv = argv
8399

100+
@eval_string = "rb_eval_string_protect(\"require '#{@debugger_loader_path}'; load_debugger(#{@gems_to_include.gsub("\"", "'")}, #{@argv.gsub("\"", "'")})\", (int *)0)"
101+
84102
launch_string = "#{self} #{executable} #{flags}"
85103
@pipe = IO.popen(launch_string, 'r+')
86104
$stdout.puts "executed '#{launch_string}'"
87105
end
88106

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+
89119
def attach_to_process
90120
execute "attach #{@pid}"
91121
end
@@ -101,15 +131,17 @@ class NativeDebugger
101131

102132
def get_response
103133
# we need this hack to understand that debugger gave us all output from last executed command
104-
@pipe.puts "print \"#{@delimiter}\""
134+
print_delimiter
105135

106136
content = ''
107137
loop do
108138
line = @pipe.readline
139+
break if check_delimiter(line)
140+
DebugPrinter.print_debug('respond line: ' + line)
109141
next if line =~ /\(lldb\)/ # lldb repeats your input to its output
110-
break if line =~ /\$\d+\s=\s"#{@delimiter}"/
111142
content += line
112143
end
144+
113145
content
114146
end
115147

@@ -121,6 +153,14 @@ class NativeDebugger
121153

122154
end
123155

156+
def print_delimiter
157+
158+
end
159+
160+
def check_delimiter(line)
161+
162+
end
163+
124164
def switch_to_thread
125165

126166
end
@@ -150,7 +190,7 @@ class NativeDebugger
150190
end
151191

152192
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+
154194
end
155195

156196
def exit
@@ -179,7 +219,6 @@ class LLDB < NativeDebugger
179219
info_threads = (execute 'thread list').split("\n")
180220
info_threads.each do |thread_info|
181221
next unless thread_info =~ /[\s*]*thread\s#\d+.*/
182-
$stdout.puts "thread_info: #{thread_info}"
183222
is_main = thread_info[0] == '*'
184223
thread_num = thread_info.sub(/[\s*]*thread\s#/, '').sub(/:\s.*$/, '').to_i
185224
thread = ProcessThread.new(thread_num, is_main, thread_info, self)
@@ -203,10 +242,22 @@ class LLDB < NativeDebugger
203242
def call_start_attach
204243
super()
205244
execute "expr (void *) dlopen(\"#{@path_to_attach}\", 2)"
206-
execute 'call start_attach()'
245+
execute 'expr (int) start_attach()'
207246
set_tbreak(@tbreak)
208247
end
209248

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+
210261
def to_s
211262
'lldb'
212263
end
@@ -257,6 +308,18 @@ class GDB < NativeDebugger
257308
set_tbreak(@tbreak)
258309
end
259310

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+
260323
def to_s
261324
'gdb'
262325
end
@@ -317,15 +380,21 @@ class ProcessThread
317380
end
318381

319382
def command_exists(command)
383+
checking_command = "checking command #{command} for existence\n"
320384
`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
321390
$?.exitstatus == 0
322391
end
323392

324393
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')
328395
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)
329398
else
330399
raise 'Neither gdb nor lldb was found. Aborting.'
331400
end
@@ -348,6 +417,7 @@ debugger.attach_to_process
348417
debugger.set_flags
349418

350419
if options.uid
420+
DebugPrinter.print_debug("changing current uid from #{Process.uid} to #{options.uid}")
351421
Process::Sys.setuid(options.uid.to_i)
352422
end
353423

0 commit comments

Comments
 (0)