ruby-debug-ide 0.6.1.beta2 → 0.6.1.beta3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|