ruby-debug-ide 0.6.1.beta2 → 0.6.1.beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/gdb_wrapper +128 -0
- data/bin/rdebug-ide +38 -11
- data/lib/ruby-debug-ide.rb +6 -21
- data/lib/ruby-debug-ide/attach/debugger_loader.rb +20 -0
- data/lib/ruby-debug-ide/attach/gdb.rb +73 -0
- data/lib/ruby-debug-ide/attach/lldb.rb +71 -0
- data/lib/ruby-debug-ide/attach/native_debugger.rb +133 -0
- data/lib/ruby-debug-ide/attach/process_thread.rb +54 -0
- data/lib/ruby-debug-ide/attach/util.rb +25 -0
- data/lib/ruby-debug-ide/boo.sh +1 -0
- data/lib/ruby-debug-ide/commands/control.rb +29 -0
- data/lib/ruby-debug-ide/foo.sh +4 -0
- data/lib/ruby-debug-ide/greeter.rb +40 -0
- data/lib/ruby-debug-ide/ide_processor.rb +0 -1
- data/lib/ruby-debug-ide/multiprocess/pre_child.rb +4 -4
- data/lib/ruby-debug-ide/version.rb +1 -1
- data/ruby-debug-ide.gemspec +1 -1
- metadata +17 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 425519e48f44723b8129340ee99f2a2741aaa07c
|
4
|
+
data.tar.gz: 99cd5ba804e4884644ac5d38465a8ab1f73cd40f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c2a4de25b4516fc011791b0d6572e57f52d06335b03b1225deb5c771bbd6cf25db7d954424f916e099f4f961719f933855e875be11f04f14a34454cdf8d638c7
|
7
|
+
data.tar.gz: 5836a80d9904b1b08b87f33b77f2806a1a3bbdc57b4a2697766bbd6c7f2ffda888fe5d2c2573b49b48cab849208a7aba699f03bdb6e97e6d2807187283ce25d0
|
data/bin/gdb_wrapper
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
$stdout.sync = true
|
7
|
+
$stderr.sync = true
|
8
|
+
|
9
|
+
options = OpenStruct.new(
|
10
|
+
'pid' => nil,
|
11
|
+
'sdk_path' => nil,
|
12
|
+
'uid' => nil,
|
13
|
+
'gems_to_include' => []
|
14
|
+
)
|
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
|
+
|
32
|
+
opts = OptionParser.new do |opts|
|
33
|
+
# TODO need some banner
|
34
|
+
opts.banner = <<EOB
|
35
|
+
Some useful banner.
|
36
|
+
EOB
|
37
|
+
|
38
|
+
opts.on('--pid PID', 'pid of process you want to attach to for debugging') do |pid|
|
39
|
+
options.pid = pid
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('--ruby-path RUBY_PATH', 'path to ruby interpreter') do |ruby_path|
|
43
|
+
options.ruby_path = ruby_path
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on('--uid UID', 'uid which this process should set after executing gdb attach') do |uid|
|
47
|
+
options.uid = uid
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on('--include-gem GEM_LIB_PATH', 'lib of gem to include') do |gem_lib_path|
|
51
|
+
options.gems_to_include << gem_lib_path
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.parse! ARGV
|
56
|
+
|
57
|
+
unless options.pid
|
58
|
+
$stderr.puts 'You should specify PID of process you want to attach to'
|
59
|
+
exit 1
|
60
|
+
end
|
61
|
+
|
62
|
+
unless options.ruby_path
|
63
|
+
$stderr.puts 'You should specify path to the ruby interpreter'
|
64
|
+
exit 1
|
65
|
+
end
|
66
|
+
|
67
|
+
argv = '["' + ARGV * '", "' + '"]'
|
68
|
+
debugger_loader_path = File.expand_path(File.dirname(__FILE__)) + '/../lib/ruby-debug-ide/attach/debugger_loader'
|
69
|
+
|
70
|
+
options.gems_to_include.each do |gem_path|
|
71
|
+
$LOAD_PATH.unshift(gem_path) unless $LOAD_PATH.include?(gem_path)
|
72
|
+
end
|
73
|
+
|
74
|
+
require 'ruby-debug-ide/greeter'
|
75
|
+
Debugger::print_greeting_msg($stdout, nil, nil)
|
76
|
+
|
77
|
+
require 'ruby-debug-ide/attach/util'
|
78
|
+
require 'ruby-debug-ide/attach/native_debugger'
|
79
|
+
require 'ruby-debug-ide/attach/process_thread'
|
80
|
+
|
81
|
+
debugger = choose_debugger(options.ruby_path, options.pid, options.gems_to_include, debugger_loader_path, argv)
|
82
|
+
|
83
|
+
trap('INT') do
|
84
|
+
unless debugger.exited?
|
85
|
+
$stderr.puts "backtraces for threads:\n\n"
|
86
|
+
process_threads = debugger.process_threads
|
87
|
+
if process_threads
|
88
|
+
process_threads.each do |thread|
|
89
|
+
$stderr.puts "#{thread.thread_info}\n#{thread.last_bt}\n\n"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
debugger.exit
|
93
|
+
end
|
94
|
+
exit!
|
95
|
+
end
|
96
|
+
|
97
|
+
debugger.attach_to_process
|
98
|
+
debugger.set_flags
|
99
|
+
|
100
|
+
if options.uid
|
101
|
+
DebugPrinter.print_debug("changing current uid from #{Process.uid} to #{options.uid}")
|
102
|
+
Process::Sys.setuid(options.uid.to_i)
|
103
|
+
end
|
104
|
+
|
105
|
+
if debugger.check_already_under_debug
|
106
|
+
$stderr.puts "Process #{debugger.pid} is already under debug"
|
107
|
+
debugger.exit
|
108
|
+
exit!
|
109
|
+
end
|
110
|
+
|
111
|
+
should_check_threads_state = true
|
112
|
+
|
113
|
+
while should_check_threads_state
|
114
|
+
should_check_threads_state = false
|
115
|
+
debugger.update_threads.each do |thread|
|
116
|
+
thread.switch
|
117
|
+
while thread.need_finish_frame
|
118
|
+
should_check_threads_state = true
|
119
|
+
thread.finish
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
debugger.wait_line_event
|
125
|
+
debugger.load_debugger
|
126
|
+
debugger.exit
|
127
|
+
|
128
|
+
sleep
|
data/bin/rdebug-ide
CHANGED
@@ -22,7 +22,9 @@ options = OpenStruct.new(
|
|
22
22
|
'evaluation_timeout' => 10,
|
23
23
|
'rm_protocol_extensions' => false,
|
24
24
|
'catchpoint_deleted_event' => false,
|
25
|
-
'value_as_nested_element' => false
|
25
|
+
'value_as_nested_element' => false,
|
26
|
+
'attach_mode' => false,
|
27
|
+
'cli_debug' => false
|
26
28
|
)
|
27
29
|
|
28
30
|
opts = OptionParser.new do |opts|
|
@@ -47,6 +49,7 @@ EOB
|
|
47
49
|
opts.on("-l", "--load-mode", "load mode (experimental)") {options.load_mode = true}
|
48
50
|
opts.on("-d", "--debug", "Debug self - prints information for debugging ruby-debug itself") do
|
49
51
|
Debugger.cli_debug = true
|
52
|
+
options.cli_debug = true
|
50
53
|
end
|
51
54
|
opts.on("--xml-debug", "Debug self - sends information <message>s for debugging ruby-debug itself") do
|
52
55
|
Debugger.xml_debug = true
|
@@ -54,7 +57,9 @@ EOB
|
|
54
57
|
opts.on("-I", "--include PATH", String, "Add PATH to $LOAD_PATH") do |path|
|
55
58
|
$LOAD_PATH.unshift(path)
|
56
59
|
end
|
57
|
-
|
60
|
+
opts.on("--attach-mode", "Tells that rdebug-ide is working in attach mode") do
|
61
|
+
options.attach_mode = true
|
62
|
+
end
|
58
63
|
opts.on("--keep-frame-binding", "Keep frame bindings") {options.frame_bind = true}
|
59
64
|
opts.on("--disable-int-handler", "Disables interrupt signal handler") {options.int_handler = false}
|
60
65
|
opts.on("--rubymine-protocol-extensions", "Enable all RubyMine-specific incompatible protocol extensions") do
|
@@ -89,29 +94,37 @@ rescue StandardError => e
|
|
89
94
|
exit(1)
|
90
95
|
end
|
91
96
|
|
92
|
-
if ARGV.empty?
|
97
|
+
if ARGV.empty? && !options.attach_mode
|
93
98
|
puts opts
|
94
99
|
puts
|
95
100
|
puts "Must specify a script to run"
|
96
101
|
exit(1)
|
97
|
-
end
|
102
|
+
end
|
98
103
|
|
99
104
|
# save script name
|
100
|
-
|
105
|
+
if !options.attach_mode
|
106
|
+
Debugger::PROG_SCRIPT = ARGV.shift
|
107
|
+
else
|
108
|
+
Debugger::PROG_SCRIPT = $0
|
109
|
+
end
|
101
110
|
|
102
111
|
if options.dispatcher_port != -1
|
103
112
|
ENV['IDE_PROCESS_DISPATCHER'] = options.dispatcher_port.to_s
|
104
113
|
if RUBY_VERSION < "1.9"
|
105
|
-
|
114
|
+
lib_path = File.expand_path(File.dirname(__FILE__) + "/../lib/")
|
115
|
+
$: << lib_path unless $:.include? lib_path
|
106
116
|
require 'ruby-debug-ide/multiprocess'
|
107
117
|
else
|
108
118
|
require_relative '../lib/ruby-debug-ide/multiprocess'
|
109
119
|
end
|
110
120
|
|
111
121
|
ENV['DEBUGGER_STORED_RUBYLIB'] = ENV['RUBYLIB']
|
112
|
-
old_opts = ENV['RUBYOPT']
|
113
|
-
|
114
|
-
|
122
|
+
old_opts = ENV['RUBYOPT'] || ''
|
123
|
+
starter = "-r#{File.expand_path(File.dirname(__FILE__))}/../lib/ruby-debug-ide/multiprocess/starter"
|
124
|
+
unless old_opts.include? starter
|
125
|
+
ENV['RUBYOPT'] = starter
|
126
|
+
ENV['RUBYOPT'] += " #{old_opts}" if old_opts != ''
|
127
|
+
end
|
115
128
|
ENV['DEBUGGER_CLI_DEBUG'] = Debugger.cli_debug.to_s
|
116
129
|
end
|
117
130
|
|
@@ -119,7 +132,7 @@ if options.int_handler
|
|
119
132
|
# install interruption handler
|
120
133
|
trap('INT') { Debugger.interrupt_last }
|
121
134
|
end
|
122
|
-
|
135
|
+
|
123
136
|
# set options
|
124
137
|
Debugger.keep_frame_binding = options.frame_bind
|
125
138
|
Debugger.tracing = options.tracing
|
@@ -127,5 +140,19 @@ Debugger.evaluation_timeout = options.evaluation_timeout
|
|
127
140
|
Debugger.catchpoint_deleted_event = options.catchpoint_deleted_event || options.rm_protocol_extensions
|
128
141
|
Debugger.value_as_nested_element = options.value_as_nested_element || options.rm_protocol_extensions
|
129
142
|
|
130
|
-
Debugger.
|
143
|
+
Debugger.attached = true
|
131
144
|
|
145
|
+
if options.attach_mode
|
146
|
+
if Debugger::FRONT_END == "debase"
|
147
|
+
Debugger.init_variables
|
148
|
+
end
|
149
|
+
|
150
|
+
Debugger::MultiProcess::pre_child(options)
|
151
|
+
|
152
|
+
if Debugger::FRONT_END == "debase"
|
153
|
+
Debugger.setup_tracepoints
|
154
|
+
Debugger.prepare_context
|
155
|
+
end
|
156
|
+
else
|
157
|
+
Debugger.debug_program(options)
|
158
|
+
end
|
data/lib/ruby-debug-ide.rb
CHANGED
@@ -4,11 +4,13 @@ require "socket"
|
|
4
4
|
require 'thread'
|
5
5
|
if RUBY_VERSION < '2.0' || defined?(JRUBY_VERSION)
|
6
6
|
require 'ruby-debug-base'
|
7
|
+
Debugger::FRONT_END = "ruby-debug-base"
|
7
8
|
else
|
8
9
|
require 'debase'
|
10
|
+
Debugger::FRONT_END = "debase"
|
9
11
|
end
|
10
12
|
|
11
|
-
require 'ruby-debug-ide/
|
13
|
+
require 'ruby-debug-ide/greeter'
|
12
14
|
require 'ruby-debug-ide/xml_printer'
|
13
15
|
require 'ruby-debug-ide/ide_processor'
|
14
16
|
require 'ruby-debug-ide/event_processor'
|
@@ -41,6 +43,7 @@ module Debugger
|
|
41
43
|
cleared
|
42
44
|
end
|
43
45
|
|
46
|
+
attr_accessor :attached
|
44
47
|
attr_accessor :cli_debug, :xml_debug, :evaluation_timeout
|
45
48
|
attr_accessor :control_thread
|
46
49
|
attr_reader :interface
|
@@ -108,9 +111,8 @@ module Debugger
|
|
108
111
|
# "localhost" and nil have problems on some systems.
|
109
112
|
host ||= '127.0.0.1'
|
110
113
|
server = TCPServer.new(host, port)
|
111
|
-
print_greeting_msg(host, port)
|
114
|
+
print_greeting_msg($stderr, host, port)
|
112
115
|
notify_dispatcher(port) if notify_dispatcher
|
113
|
-
|
114
116
|
while (session = server.accept)
|
115
117
|
$stderr.puts "Connected from #{session.peeraddr[2]}" if Debugger.cli_debug
|
116
118
|
dispatcher = ENV['IDE_PROCESS_DISPATCHER']
|
@@ -136,23 +138,6 @@ module Debugger
|
|
136
138
|
end
|
137
139
|
end
|
138
140
|
|
139
|
-
def print_greeting_msg(host, port)
|
140
|
-
base_gem_name = if defined?(JRUBY_VERSION) || RUBY_VERSION < '1.9.0'
|
141
|
-
'ruby-debug-base'
|
142
|
-
elsif RUBY_VERSION < '2.0.0'
|
143
|
-
'ruby-debug-base19x'
|
144
|
-
else
|
145
|
-
'debase'
|
146
|
-
end
|
147
|
-
|
148
|
-
file_filtering_support = if Command.file_filter_supported?
|
149
|
-
'supported'
|
150
|
-
else
|
151
|
-
'not supported'
|
152
|
-
end
|
153
|
-
$stderr.printf "Fast Debugger (ruby-debug-ide #{IDE_VERSION}, #{base_gem_name} #{VERSION}, file filtering is #{file_filtering_support}) listens on #{host}:#{port}\n"
|
154
|
-
end
|
155
|
-
|
156
141
|
private
|
157
142
|
|
158
143
|
|
@@ -160,8 +145,8 @@ module Debugger
|
|
160
145
|
return unless ENV['IDE_PROCESS_DISPATCHER']
|
161
146
|
acceptor_host, acceptor_port = ENV['IDE_PROCESS_DISPATCHER'].split(":")
|
162
147
|
acceptor_host, acceptor_port = '127.0.0.1', acceptor_host unless acceptor_port
|
163
|
-
|
164
148
|
connected = false
|
149
|
+
|
165
150
|
3.times do |i|
|
166
151
|
begin
|
167
152
|
s = TCPSocket.open(acceptor_host, acceptor_port)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
def load_debugger(gems_to_include, new_argv)
|
2
|
+
path_to_rdebug = File.expand_path(File.dirname(__FILE__)) + '/../../../bin/rdebug-ide'
|
3
|
+
|
4
|
+
old_argv = ARGV.clone
|
5
|
+
ARGV.clear
|
6
|
+
new_argv.each do |x|
|
7
|
+
ARGV << x
|
8
|
+
end
|
9
|
+
|
10
|
+
gems_to_include.each do |gem_path|
|
11
|
+
$LOAD_PATH.unshift(gem_path) unless $LOAD_PATH.include?(gem_path)
|
12
|
+
end
|
13
|
+
|
14
|
+
load path_to_rdebug
|
15
|
+
|
16
|
+
ARGV.clear
|
17
|
+
old_argv.each do |x|
|
18
|
+
ARGV << x
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'ruby-debug-ide/attach/native_debugger'
|
2
|
+
|
3
|
+
class GDB < NativeDebugger
|
4
|
+
|
5
|
+
def initialize(executable, pid, flags, gems_to_include, debugger_loader_path, argv)
|
6
|
+
super(executable, pid, flags, gems_to_include, debugger_loader_path, argv)
|
7
|
+
end
|
8
|
+
|
9
|
+
def set_flags
|
10
|
+
execute 'set scheduler-locking off' # we will deadlock with it
|
11
|
+
execute 'set unwindonsignal on' # in case of some signal we will exit gdb
|
12
|
+
end
|
13
|
+
|
14
|
+
def update_threads
|
15
|
+
@process_threads = []
|
16
|
+
info_threads = (execute 'info threads').split("\n")
|
17
|
+
info_threads.each do |thread_info|
|
18
|
+
next unless thread_info =~ /[\s*]*\d+\s+Thread.*/
|
19
|
+
$stdout.puts "thread_info: #{thread_info}"
|
20
|
+
is_main = thread_info[0] == '*'
|
21
|
+
thread_num = thread_info.sub(/[\s*]*/, '').sub(/\s.*$/, '').to_i
|
22
|
+
thread = ProcessThread.new(thread_num, is_main, thread_info, self)
|
23
|
+
if thread.is_main
|
24
|
+
@main_thread = thread
|
25
|
+
end
|
26
|
+
@process_threads << thread
|
27
|
+
end
|
28
|
+
@process_threads
|
29
|
+
end
|
30
|
+
|
31
|
+
def check_already_under_debug
|
32
|
+
threads = execute 'info threads'
|
33
|
+
threads =~ /ruby-debug-ide/
|
34
|
+
end
|
35
|
+
|
36
|
+
def switch_to_thread(thread_num)
|
37
|
+
execute "thread #{thread_num}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_break(str)
|
41
|
+
execute "tbreak #{str}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def call_start_attach
|
45
|
+
super()
|
46
|
+
execute "call dlopen(\"#{@path_to_attach}\", 2)"
|
47
|
+
execute 'call debase_start_attach()'
|
48
|
+
set_break(@tbreak)
|
49
|
+
end
|
50
|
+
|
51
|
+
def print_delimiter
|
52
|
+
@pipe.puts "print \"#{@delimiter}\""
|
53
|
+
end
|
54
|
+
|
55
|
+
def check_delimiter(line)
|
56
|
+
line =~ /\$\d+\s=\s"#{@delimiter}"/
|
57
|
+
end
|
58
|
+
|
59
|
+
def load_debugger
|
60
|
+
execute "call #{@eval_string}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_s
|
64
|
+
GDB.to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
class << self
|
68
|
+
def to_s
|
69
|
+
'gdb'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'ruby-debug-ide/attach/native_debugger'
|
2
|
+
|
3
|
+
class LLDB < NativeDebugger
|
4
|
+
|
5
|
+
def initialize(executable, pid, flags, gems_to_include, debugger_loader_path, argv)
|
6
|
+
super(executable, pid, flags, gems_to_include, debugger_loader_path, argv)
|
7
|
+
end
|
8
|
+
|
9
|
+
def set_flags
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
def update_threads
|
14
|
+
@process_threads = []
|
15
|
+
info_threads = (execute 'thread list').split("\n")
|
16
|
+
info_threads.each do |thread_info|
|
17
|
+
next unless thread_info =~ /[\s*]*thread\s#\d+.*/
|
18
|
+
is_main = thread_info[0] == '*'
|
19
|
+
thread_num = thread_info.sub(/[\s*]*thread\s#/, '').sub(/:\s.*$/, '').to_i
|
20
|
+
thread = ProcessThread.new(thread_num, is_main, thread_info, self)
|
21
|
+
if thread.is_main
|
22
|
+
@main_thread = thread
|
23
|
+
end
|
24
|
+
@process_threads << thread
|
25
|
+
end
|
26
|
+
@process_threads
|
27
|
+
end
|
28
|
+
|
29
|
+
def check_already_under_debug
|
30
|
+
threads = execute 'thread list'
|
31
|
+
threads =~ /ruby-debug-ide/
|
32
|
+
end
|
33
|
+
|
34
|
+
def switch_to_thread(thread_num)
|
35
|
+
execute "thread select #{thread_num}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_break(str)
|
39
|
+
execute "breakpoint set --shlib #{@path_to_attach} --name #{str}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def call_start_attach
|
43
|
+
super()
|
44
|
+
execute "expr (void *) dlopen(\"#{@path_to_attach}\", 2)"
|
45
|
+
execute 'expr (int) debase_start_attach()'
|
46
|
+
set_break(@tbreak)
|
47
|
+
end
|
48
|
+
|
49
|
+
def print_delimiter
|
50
|
+
@pipe.puts "script print \"#{@delimiter}\""
|
51
|
+
end
|
52
|
+
|
53
|
+
def check_delimiter(line)
|
54
|
+
line =~ /#{@delimiter}$/
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_debugger
|
58
|
+
execute "expr (void) #{@eval_string}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
LLDB.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
class << self
|
66
|
+
def to_s
|
67
|
+
'lldb'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
class NativeDebugger
|
2
|
+
|
3
|
+
attr_reader :pid, :main_thread, :process_threads, :pipe
|
4
|
+
|
5
|
+
# @param executable -- path to ruby interpreter
|
6
|
+
# @param pid -- pid of process you want to debug
|
7
|
+
# @param flags -- flags you want to specify to your debugger as a string (e.g. "-nx -nh" for gdb to disable .gdbinit)
|
8
|
+
def initialize(executable, pid, flags, gems_to_include, debugger_loader_path, argv)
|
9
|
+
@pid = pid
|
10
|
+
@delimiter = '__OUTPUT_FINISHED__' # for getting response
|
11
|
+
@tbreak = '__func_to_set_breakpoint_at'
|
12
|
+
@main_thread = nil
|
13
|
+
@process_threads = nil
|
14
|
+
debase_path = gems_to_include.select {|gem_path| gem_path =~ /debase/}
|
15
|
+
if debase_path.size == 0
|
16
|
+
raise 'No debase gem found.'
|
17
|
+
end
|
18
|
+
@path_to_attach = find_attach_lib(debase_path[0])
|
19
|
+
|
20
|
+
@gems_to_include = '["' + gems_to_include * '", "' + '"]'
|
21
|
+
@debugger_loader_path = debugger_loader_path
|
22
|
+
@argv = argv
|
23
|
+
|
24
|
+
@eval_string = "debase_rb_eval(\"require '#{@debugger_loader_path}'; load_debugger(#{@gems_to_include.gsub("\"", "'")}, #{@argv.gsub("\"", "'")})\")"
|
25
|
+
|
26
|
+
launch_string = "#{self} #{executable} #{flags}"
|
27
|
+
@pipe = IO.popen(launch_string, 'r+')
|
28
|
+
$stdout.puts "executed '#{launch_string}'"
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_attach_lib(debase_path)
|
32
|
+
attach_lib = debase_path + '/attach'
|
33
|
+
known_extensions = %w(.so .bundle .dll .dylib)
|
34
|
+
known_extensions.each do |ext|
|
35
|
+
if File.file?(attach_lib + ext)
|
36
|
+
return attach_lib + ext
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
raise 'Could not find attach library'
|
41
|
+
end
|
42
|
+
|
43
|
+
def attach_to_process
|
44
|
+
execute "attach #{@pid}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def execute(command)
|
48
|
+
@pipe.puts command
|
49
|
+
$stdout.puts "executed `#{command}` command inside #{self}."
|
50
|
+
if command == 'q'
|
51
|
+
return ''
|
52
|
+
end
|
53
|
+
get_response
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_response
|
57
|
+
# we need this hack to understand that debugger gave us all output from last executed command
|
58
|
+
print_delimiter
|
59
|
+
|
60
|
+
content = ''
|
61
|
+
loop do
|
62
|
+
line = @pipe.readline
|
63
|
+
DebugPrinter.print_debug('respond line: ' + line)
|
64
|
+
break if check_delimiter(line)
|
65
|
+
next if line =~ /\(lldb\)/ # lldb repeats your input to its output
|
66
|
+
content += line
|
67
|
+
end
|
68
|
+
|
69
|
+
content
|
70
|
+
end
|
71
|
+
|
72
|
+
def update_threads
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
def check_already_under_debug
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
def print_delimiter
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
def check_delimiter(line)
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
def switch_to_thread
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
def set_break(str)
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
def continue
|
97
|
+
$stdout.puts 'continuing'
|
98
|
+
@pipe.puts 'c'
|
99
|
+
loop do
|
100
|
+
line = @pipe.readline
|
101
|
+
DebugPrinter.print_debug('respond line: ' + line)
|
102
|
+
break if line =~ /#{Regexp.escape(@tbreak)}/
|
103
|
+
end
|
104
|
+
get_response
|
105
|
+
end
|
106
|
+
|
107
|
+
def call_start_attach
|
108
|
+
raise 'No main thread found. Did you forget to call `update_threads`?' if @main_thread == nil
|
109
|
+
@main_thread.switch
|
110
|
+
end
|
111
|
+
|
112
|
+
def wait_line_event
|
113
|
+
call_start_attach
|
114
|
+
continue
|
115
|
+
end
|
116
|
+
|
117
|
+
def load_debugger
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
def exited?
|
122
|
+
@pipe.closed?
|
123
|
+
end
|
124
|
+
|
125
|
+
def exit
|
126
|
+
@pipe.close
|
127
|
+
end
|
128
|
+
|
129
|
+
def to_s
|
130
|
+
'native_debugger'
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'ruby-debug-ide/attach/native_debugger'
|
2
|
+
|
3
|
+
class ProcessThread
|
4
|
+
|
5
|
+
attr_reader :thread_num, :is_main, :thread_info, :last_bt
|
6
|
+
|
7
|
+
def initialize(thread_num, is_main, thread_info, native_debugger)
|
8
|
+
@thread_num = thread_num
|
9
|
+
@is_main = is_main
|
10
|
+
@native_debugger = native_debugger
|
11
|
+
@thread_info = thread_info
|
12
|
+
@last_bt = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def switch
|
16
|
+
@native_debugger.switch_to_thread(thread_num)
|
17
|
+
end
|
18
|
+
|
19
|
+
def finish
|
20
|
+
@native_debugger.execute 'finish'
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_bt
|
24
|
+
@last_bt = @native_debugger.execute 'bt'
|
25
|
+
end
|
26
|
+
|
27
|
+
def any_caller_match(bt, pattern)
|
28
|
+
bt =~ /#{pattern}/
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_inside_malloc(bt = get_bt)
|
32
|
+
if any_caller_match(bt, '(malloc)')
|
33
|
+
$stderr.puts "process #{@native_debugger.pid} is currently inside malloc."
|
34
|
+
true
|
35
|
+
else
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def is_inside_gc(bt = get_bt)
|
41
|
+
if any_caller_match(bt, '(gc\.c)')
|
42
|
+
$stderr.puts "process #{@native_debugger.pid} is currently in garbage collection phase."
|
43
|
+
true
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def need_finish_frame
|
50
|
+
bt = get_bt
|
51
|
+
is_inside_malloc(bt) || is_inside_gc(bt)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'ruby-debug-ide/attach/lldb'
|
2
|
+
require 'ruby-debug-ide/attach/gdb'
|
3
|
+
|
4
|
+
def command_exists(command)
|
5
|
+
checking_command = "checking command #{command} for existence\n"
|
6
|
+
`command -v #{command} >/dev/null 2>&1 || { exit 1; }`
|
7
|
+
if $?.exitstatus != 0
|
8
|
+
DebugPrinter.print_debug("#{checking_command}command does not exist.")
|
9
|
+
else
|
10
|
+
DebugPrinter.print_debug("#{checking_command}command does exist.")
|
11
|
+
end
|
12
|
+
$?.exitstatus == 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def choose_debugger(ruby_path, pid, gems_to_include, debugger_loader_path, argv)
|
16
|
+
if command_exists(LLDB.to_s)
|
17
|
+
debugger = LLDB.new(ruby_path, pid, '--no-lldbinit', gems_to_include, debugger_loader_path, argv)
|
18
|
+
elsif command_exists(GDB.to_s)
|
19
|
+
debugger = GDB.new(ruby_path, pid, '-nh -nx', gems_to_include, debugger_loader_path, argv)
|
20
|
+
else
|
21
|
+
raise 'Neither gdb nor lldb was found. Aborting.'
|
22
|
+
end
|
23
|
+
|
24
|
+
debugger
|
25
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
sh foo.sh
|
@@ -126,4 +126,33 @@ module Debugger
|
|
126
126
|
end
|
127
127
|
end
|
128
128
|
end
|
129
|
+
|
130
|
+
|
131
|
+
class DetachCommand < Command # :nodoc:
|
132
|
+
self.control = true
|
133
|
+
|
134
|
+
def regexp
|
135
|
+
/^\s*detach\s*$/
|
136
|
+
end
|
137
|
+
|
138
|
+
def execute
|
139
|
+
Debugger.attached = false
|
140
|
+
Debugger.stop
|
141
|
+
Debugger.interface.close
|
142
|
+
Debugger.control_thread = nil
|
143
|
+
Thread.current.exit #@control_thread is a current thread
|
144
|
+
end
|
145
|
+
|
146
|
+
class << self
|
147
|
+
def help_command
|
148
|
+
'detach'
|
149
|
+
end
|
150
|
+
|
151
|
+
def help(cmd)
|
152
|
+
%{
|
153
|
+
detach\ndetach debugger\nnote: this option is only for remote debugging (or local attach)
|
154
|
+
}
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
129
158
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
if RUBY_VERSION < '2.0' || defined?(JRUBY_VERSION)
|
2
|
+
require 'ruby-debug-base'
|
3
|
+
else
|
4
|
+
require 'debase'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'ruby-debug-ide/version'
|
8
|
+
require 'ruby-debug-ide/ide_processor'
|
9
|
+
|
10
|
+
module Debugger
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def print_greeting_msg(stream, host, port)
|
14
|
+
base_gem_name = if defined?(JRUBY_VERSION) || RUBY_VERSION < '1.9.0'
|
15
|
+
'ruby-debug-base'
|
16
|
+
elsif RUBY_VERSION < '2.0.0'
|
17
|
+
'ruby-debug-base19x'
|
18
|
+
else
|
19
|
+
'debase'
|
20
|
+
end
|
21
|
+
|
22
|
+
file_filtering_support = if Command.file_filter_supported?
|
23
|
+
'supported'
|
24
|
+
else
|
25
|
+
'not supported'
|
26
|
+
end
|
27
|
+
|
28
|
+
if host && port
|
29
|
+
listens_on = " listens on #{host}:#{port}\n"
|
30
|
+
else
|
31
|
+
listens_on = "\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
msg = "Fast Debugger (ruby-debug-ide #{IDE_VERSION}, #{base_gem_name} #{VERSION}, file filtering is #{file_filtering_support})" + listens_on
|
35
|
+
|
36
|
+
stream.printf msg
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -77,7 +77,6 @@ module Debugger
|
|
77
77
|
ctrl_cmd_classes = Command.commands.select{|cmd| cmd.control}
|
78
78
|
state = ControlState.new(@interface)
|
79
79
|
ctrl_cmds = ctrl_cmd_classes.map{|cmd| cmd.new(state, @printer)}
|
80
|
-
|
81
80
|
while input = @interface.read_command
|
82
81
|
# escape % since print_debug might use printf
|
83
82
|
# sleep 0.3
|
@@ -1,19 +1,19 @@
|
|
1
1
|
module Debugger
|
2
2
|
module MultiProcess
|
3
3
|
class << self
|
4
|
-
def pre_child
|
4
|
+
def pre_child(options = nil)
|
5
|
+
return unless Debugger.attached
|
5
6
|
|
6
7
|
require 'socket'
|
7
8
|
require 'ostruct'
|
8
9
|
|
9
10
|
host = ENV['DEBUGGER_HOST']
|
10
|
-
port = find_free_port(host)
|
11
11
|
|
12
|
-
options
|
12
|
+
options ||= OpenStruct.new(
|
13
13
|
'frame_bind' => false,
|
14
14
|
'host' => host,
|
15
15
|
'load_mode' => false,
|
16
|
-
'port' =>
|
16
|
+
'port' => find_free_port(host),
|
17
17
|
'stop' => false,
|
18
18
|
'tracing' => false,
|
19
19
|
'int_handler' => true,
|
data/ruby-debug-ide.gemspec
CHANGED
@@ -36,7 +36,7 @@ EOF
|
|
36
36
|
spec.platform = Gem::Platform::RUBY
|
37
37
|
spec.require_path = "lib"
|
38
38
|
spec.bindir = "bin"
|
39
|
-
spec.executables = ["rdebug-ide"]
|
39
|
+
spec.executables = ["rdebug-ide", "gdb_wrapper"]
|
40
40
|
spec.files = FILES
|
41
41
|
|
42
42
|
spec.extensions << "ext/mkrf_conf.rb" unless ENV['NO_EXT']
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-debug-ide
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.1.
|
4
|
+
version: 0.6.1.beta3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Markus Barchfeld, Martin Krauskopf, Mark Moseley, JetBrains RubyMine Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -24,13 +24,12 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.8.1
|
27
|
-
description:
|
28
|
-
and RubyMine.
|
29
|
-
|
30
|
-
'
|
27
|
+
description: |
|
28
|
+
An interface which glues ruby-debug to IDEs like Eclipse (RDT), NetBeans and RubyMine.
|
31
29
|
email: rubymine-feedback@jetbrains.com
|
32
30
|
executables:
|
33
31
|
- rdebug-ide
|
32
|
+
- gdb_wrapper
|
34
33
|
extensions:
|
35
34
|
- ext/mkrf_conf.rb
|
36
35
|
extra_rdoc_files: []
|
@@ -41,9 +40,17 @@ files:
|
|
41
40
|
- Gemfile
|
42
41
|
- MIT-LICENSE
|
43
42
|
- Rakefile
|
43
|
+
- bin/gdb_wrapper
|
44
44
|
- bin/rdebug-ide
|
45
45
|
- ext/mkrf_conf.rb
|
46
46
|
- lib/ruby-debug-ide.rb
|
47
|
+
- lib/ruby-debug-ide/attach/debugger_loader.rb
|
48
|
+
- lib/ruby-debug-ide/attach/gdb.rb
|
49
|
+
- lib/ruby-debug-ide/attach/lldb.rb
|
50
|
+
- lib/ruby-debug-ide/attach/native_debugger.rb
|
51
|
+
- lib/ruby-debug-ide/attach/process_thread.rb
|
52
|
+
- lib/ruby-debug-ide/attach/util.rb
|
53
|
+
- lib/ruby-debug-ide/boo.sh
|
47
54
|
- lib/ruby-debug-ide/command.rb
|
48
55
|
- lib/ruby-debug-ide/commands/breakpoints.rb
|
49
56
|
- lib/ruby-debug-ide/commands/catchpoint.rb
|
@@ -63,6 +70,8 @@ files:
|
|
63
70
|
- lib/ruby-debug-ide/commands/threads.rb
|
64
71
|
- lib/ruby-debug-ide/commands/variables.rb
|
65
72
|
- lib/ruby-debug-ide/event_processor.rb
|
73
|
+
- lib/ruby-debug-ide/foo.sh
|
74
|
+
- lib/ruby-debug-ide/greeter.rb
|
66
75
|
- lib/ruby-debug-ide/helper.rb
|
67
76
|
- lib/ruby-debug-ide/ide_processor.rb
|
68
77
|
- lib/ruby-debug-ide/interface.rb
|
@@ -93,8 +102,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
102
|
version: 1.3.1
|
94
103
|
requirements: []
|
95
104
|
rubyforge_project: debug-commons
|
96
|
-
rubygems_version: 2.
|
105
|
+
rubygems_version: 2.4.8
|
97
106
|
signing_key:
|
98
107
|
specification_version: 4
|
99
108
|
summary: IDE interface for ruby-debug.
|
100
109
|
test_files: []
|
110
|
+
has_rdoc: false
|