Skip to content

Commit

Permalink
Merge pull request #83 from equivalence1/ruby-attach-to-process
Browse files Browse the repository at this point in the history
Choose lldb over gdb and find proper extension of library
  • Loading branch information
denofevil authored Oct 4, 2016
2 parents a193e6b + f3955ca commit f56827b
Showing 1 changed file with 79 additions and 9 deletions.
88 changes: 79 additions & 9 deletions bin/gdb_wrapper
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,22 @@ options = OpenStruct.new(
'gems_to_include' => []
)

module DebugPrinter

class << self
attr_accessor :cli_debug

def print_debug(msg)
if DebugPrinter.cli_debug
$stderr.puts msg
end
end
end

end

DebugPrinter.cli_debug = ARGV.include? '--debug'

opts = OptionParser.new do |opts|
# TODO need some banner
opts.banner = <<EOB
Expand Down Expand Up @@ -75,17 +91,31 @@ class NativeDebugger
if debase_path.size == 0
raise 'No debase gem found.'
end
@path_to_attach = debase_path[0] + '/attach.so'
@path_to_attach = find_attach_lib(debase_path[0])

@gems_to_include = '["' + gems_to_include * '", "' + '"]'
@debugger_loader_path = debugger_loader_path
@argv = argv

@eval_string = "rb_eval_string_protect(\"require '#{@debugger_loader_path}'; load_debugger(#{@gems_to_include.gsub("\"", "'")}, #{@argv.gsub("\"", "'")})\", (int *)0)"

launch_string = "#{self} #{executable} #{flags}"
@pipe = IO.popen(launch_string, 'r+')
$stdout.puts "executed '#{launch_string}'"
end

def find_attach_lib(debase_path)
attach_lib = debase_path + '/attach'
known_extensions = %w(.so .bundle .dll)
known_extensions.each do |ext|
if File.file?(attach_lib + ext)
return attach_lib + ext
end
end

raise 'Could not find attach library'
end

def attach_to_process
execute "attach #{@pid}"
end
Expand All @@ -101,15 +131,17 @@ class NativeDebugger

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

content = ''
loop do
line = @pipe.readline
break if check_delimiter(line)
DebugPrinter.print_debug('respond line: ' + line)
next if line =~ /\(lldb\)/ # lldb repeats your input to its output
break if line =~ /\$\d+\s=\s"#{@delimiter}"/
content += line
end

content
end

Expand All @@ -121,6 +153,14 @@ class NativeDebugger

end

def print_delimiter

end

def check_delimiter(line)

end

def switch_to_thread

end
Expand Down Expand Up @@ -150,7 +190,7 @@ class NativeDebugger
end

def load_debugger
execute "call rb_eval_string_protect(\"require '#{@debugger_loader_path}'; load_debugger(#{@gems_to_include.gsub("\"", "'")}, #{@argv.gsub("\"", "'")})\", (int *)0)"

end

def exit
Expand Down Expand Up @@ -179,7 +219,6 @@ class LLDB < NativeDebugger
info_threads = (execute 'thread list').split("\n")
info_threads.each do |thread_info|
next unless thread_info =~ /[\s*]*thread\s#\d+.*/
$stdout.puts "thread_info: #{thread_info}"
is_main = thread_info[0] == '*'
thread_num = thread_info.sub(/[\s*]*thread\s#/, '').sub(/:\s.*$/, '').to_i
thread = ProcessThread.new(thread_num, is_main, thread_info, self)
Expand All @@ -203,10 +242,22 @@ class LLDB < NativeDebugger
def call_start_attach
super()
execute "expr (void *) dlopen(\"#{@path_to_attach}\", 2)"
execute 'call start_attach()'
execute 'expr (int) start_attach()'
set_tbreak(@tbreak)
end

def print_delimiter
@pipe.puts "script print \"#{@delimiter}\""
end

def check_delimiter(line)
line =~ /#{@delimiter}$/
end

def load_debugger
execute "expr (VALUE) #{@eval_string}"
end

def to_s
'lldb'
end
Expand Down Expand Up @@ -257,6 +308,18 @@ class GDB < NativeDebugger
set_tbreak(@tbreak)
end

def print_delimiter
@pipe.puts "print \"#{@delimiter}\""
end

def check_delimiter(line)
line =~ /\$\d+\s=\s"#{@delimiter}"/
end

def load_debugger
execute "call #{@eval_string}"
end

def to_s
'gdb'
end
Expand Down Expand Up @@ -317,15 +380,21 @@ class ProcessThread
end

def command_exists(command)
checking_command = "checking command #{command} for existence\n"
`command -v #{command} >/dev/null 2>&1 || { exit 1; }`
if $?.exitstatus != 0
DebugPrinter.print_debug("#{checking_command}command does not exist.")
else
DebugPrinter.print_debug("#{checking_command}command does exist.")
end
$?.exitstatus == 0
end

def choose_debugger(ruby_path, pid, gems_to_include, debugger_loader_path, argv)
if command_exists('gdb')
debugger = GDB.new(ruby_path, pid, '-nh -nx', gems_to_include, debugger_loader_path, argv)
elsif command_exists('lldb')
if command_exists('lldb')
debugger = LLDB.new(ruby_path, pid, '--no-lldbinit', gems_to_include, debugger_loader_path, argv)
elsif command_exists('gdb')
debugger = GDB.new(ruby_path, pid, '-nh -nx', gems_to_include, debugger_loader_path, argv)
else
raise 'Neither gdb nor lldb was found. Aborting.'
end
Expand All @@ -348,6 +417,7 @@ debugger.attach_to_process
debugger.set_flags

if options.uid
DebugPrinter.print_debug("changing current uid from #{Process.uid} to #{options.uid}")
Process::Sys.setuid(options.uid.to_i)
end

Expand Down

0 comments on commit f56827b

Please sign in to comment.