|
| 1 | +#!/usr/bin/env ruby |
| 2 | + |
| 3 | +require 'optparse' |
| 4 | +require 'ostruct' |
| 5 | + |
| 6 | +options = OpenStruct.new( |
| 7 | + 'pid' => nil, |
| 8 | + 'sdk_path' => nil, |
| 9 | + 'uid' => nil, |
| 10 | + 'gems_to_include' => [] |
| 11 | +) |
| 12 | + |
| 13 | +opts = OptionParser.new do |opts| |
| 14 | + # TODO need some banner |
| 15 | + opts.banner = <<EOB |
| 16 | +Some usefull banner. |
| 17 | +EOB |
| 18 | + |
| 19 | + opts.on("--pid PID", "pid of process you want to attach to for debugging") do |pid| |
| 20 | + options.pid = pid |
| 21 | + end |
| 22 | + |
| 23 | + opts.on("--sdk-path SDK_PATH", "path to ruby interpreter") do |sdk_path| |
| 24 | + options.sdk_path = sdk_path |
| 25 | + end |
| 26 | + |
| 27 | + opts.on("--uid UID", "uid which this process should set after executing gdb attach") do |uid| |
| 28 | + options.uid = uid |
| 29 | + end |
| 30 | + |
| 31 | + opts.on("--include-gem GEM_LIB_PATH", "lib of gem to include") do |gem_lib_path| |
| 32 | + options.gems_to_include << gem_lib_path |
| 33 | + end |
| 34 | +end |
| 35 | + |
| 36 | +opts.parse! ARGV |
| 37 | + |
| 38 | +unless options.pid |
| 39 | + $stderr.puts "You must specify PID of process you want to attach to" |
| 40 | + exit 1 |
| 41 | +end |
| 42 | + |
| 43 | +unless options.sdk_path |
| 44 | + $stderr.puts "You must specify SDK_PATH of ruby interpreter" |
| 45 | + exit 1 |
| 46 | +end |
| 47 | + |
| 48 | +# TODO Denis told not to implement this hack |
| 49 | +# So this is only for me while debugging as |
| 50 | +# I don't want to get any warnings. |
| 51 | +sigints_caught = 0 |
| 52 | +trap('INT') do |
| 53 | + sigints_caught += 1 |
| 54 | + if sigints_caught == 2 |
| 55 | + exit 0 |
| 56 | + end |
| 57 | +end |
| 58 | + |
| 59 | +argv = '["' + ARGV * '", "' + '"]' |
| 60 | +gems_to_include = '["' + options.gems_to_include * '", "' + '"]' |
| 61 | + |
| 62 | +commands_list = [] |
| 63 | + |
| 64 | +def commands_list.add_command(command) |
| 65 | + self << "-ex \"#{command}\"" |
| 66 | +end |
| 67 | + |
| 68 | +path_to_debugger_loader = File.expand_path(File.dirname(__FILE__)) + "/../lib/ruby-debug-ide/attach/debugger_loader" |
| 69 | + |
| 70 | +# rb_finish: wait while execution comes to the next line (this is essential!) |
| 71 | +commands_list.add_command("call rb_eval_string_protect(\\\"set_trace_func lambda{|event, file, line, id, binding, classname| if /line/ =~ event; sleep 0; set_trace_func(nil); end}\\\", (int *)0)") |
| 72 | +commands_list.add_command("tbreak rb_f_sleep") |
| 73 | +commands_list.add_command("cont") |
| 74 | + |
| 75 | +# evalr: loading debugger into the process |
| 76 | +evalr = "call rb_eval_string_protect(%s, (int *)0)" |
| 77 | +commands_list.add_command("#{evalr}" % ["(\\\"require '#{path_to_debugger_loader}'; load_debugger(#{gems_to_include.gsub("\"", "'")}, #{argv.gsub("\"", "'")})\\\")"]) |
| 78 | + |
| 79 | +# q: exit gdb and continue process execution with debugger |
| 80 | +commands_list.add_command("q") |
| 81 | + |
| 82 | +cmd = "gdb #{options.sdk_path} #{options.pid} -nh -nx -batch #{commands_list.join(" ")}" |
| 83 | + |
| 84 | +$stderr.puts "Fast Debugger " |
| 85 | +$stderr.puts "Running command #{cmd}" |
| 86 | + |
| 87 | +`#{cmd}` or raise "GDB failed. Aborting." |
| 88 | + |
| 89 | +if options.uid |
| 90 | + Process::Sys.setuid(options.uid.to_i) |
| 91 | +end |
| 92 | + |
| 93 | +sleep |
0 commit comments