Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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