ruby-debug-ide22 0.7.4 → 0.7.5
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/CHANGES +75 -75
- data/ChangeLog.archive +1073 -1073
- data/ChangeLog.md +594 -594
- data/Gemfile +38 -38
- data/MIT-LICENSE +24 -24
- data/Rakefile +92 -92
- data/bin/gdb_wrapper +96 -96
- data/bin/rdebug-ide +200 -200
- data/ext/mkrf_conf.rb +44 -44
- data/lib/ruby-debug-ide/attach/debugger_loader.rb +20 -20
- data/lib/ruby-debug-ide/attach/gdb.rb +73 -73
- data/lib/ruby-debug-ide/attach/lldb.rb +71 -71
- data/lib/ruby-debug-ide/attach/native_debugger.rb +133 -133
- data/lib/ruby-debug-ide/attach/process_thread.rb +54 -54
- data/lib/ruby-debug-ide/attach/util.rb +114 -114
- data/lib/ruby-debug-ide/command.rb +187 -187
- data/lib/ruby-debug-ide/commands/breakpoints.rb +128 -128
- data/lib/ruby-debug-ide/commands/catchpoint.rb +64 -64
- data/lib/ruby-debug-ide/commands/condition.rb +51 -51
- data/lib/ruby-debug-ide/commands/control.rb +164 -158
- data/lib/ruby-debug-ide/commands/enable.rb +203 -203
- data/lib/ruby-debug-ide/commands/eval.rb +64 -64
- data/lib/ruby-debug-ide/commands/expression_info.rb +71 -71
- data/lib/ruby-debug-ide/commands/file_filtering.rb +106 -106
- data/lib/ruby-debug-ide/commands/frame.rb +155 -155
- data/lib/ruby-debug-ide/commands/inspect.rb +25 -25
- data/lib/ruby-debug-ide/commands/load.rb +17 -17
- data/lib/ruby-debug-ide/commands/stepping.rb +108 -108
- data/lib/ruby-debug-ide/commands/threads.rb +178 -178
- data/lib/ruby-debug-ide/commands/variables.rb +154 -154
- data/lib/ruby-debug-ide/event_processor.rb +71 -71
- data/lib/ruby-debug-ide/greeter.rb +42 -42
- data/lib/ruby-debug-ide/helper.rb +33 -33
- data/lib/ruby-debug-ide/ide_processor.rb +155 -155
- data/lib/ruby-debug-ide/interface.rb +47 -45
- data/lib/ruby-debug-ide/multiprocess/monkey.rb +46 -46
- data/lib/ruby-debug-ide/multiprocess/pre_child.rb +58 -58
- data/lib/ruby-debug-ide/multiprocess/starter.rb +10 -10
- data/lib/ruby-debug-ide/multiprocess/unmonkey.rb +30 -30
- data/lib/ruby-debug-ide/multiprocess.rb +22 -22
- data/lib/ruby-debug-ide/thread_alias.rb +26 -26
- data/lib/ruby-debug-ide/version.rb +3 -3
- data/lib/ruby-debug-ide/xml_printer.rb +570 -570
- data/lib/ruby-debug-ide.rb +230 -228
- data/ruby-debug-ide.gemspec +47 -47
- metadata +4 -4
@@ -1,115 +1,115 @@
|
|
1
|
-
require 'ruby-debug-ide/attach/lldb'
|
2
|
-
require 'ruby-debug-ide/attach/gdb'
|
3
|
-
require 'socket'
|
4
|
-
require 'set'
|
5
|
-
|
6
|
-
def attach_and_return_thread(options, pid, debugger_loader_path, argv)
|
7
|
-
Thread.new(argv) do |argv|
|
8
|
-
|
9
|
-
debugger = choose_debugger(options.ruby_path, pid, options.gems_to_include, debugger_loader_path, argv)
|
10
|
-
|
11
|
-
trap('INT') do
|
12
|
-
unless debugger.exited?
|
13
|
-
$stderr.puts "backtraces for threads:\n\n"
|
14
|
-
process_threads = debugger.process_threads
|
15
|
-
if process_threads
|
16
|
-
process_threads.each do |thread|
|
17
|
-
$stderr.puts "#{thread.thread_info}\n#{thread.last_bt}\n\n"
|
18
|
-
end
|
19
|
-
end
|
20
|
-
debugger.exit
|
21
|
-
end
|
22
|
-
exit!
|
23
|
-
end
|
24
|
-
|
25
|
-
debugger.attach_to_process
|
26
|
-
debugger.set_flags
|
27
|
-
|
28
|
-
if debugger.check_already_under_debug
|
29
|
-
$stderr.puts "Process #{debugger.pid} is already under debug"
|
30
|
-
debugger.exit
|
31
|
-
exit!
|
32
|
-
end
|
33
|
-
|
34
|
-
should_check_threads_state = true
|
35
|
-
|
36
|
-
while should_check_threads_state
|
37
|
-
should_check_threads_state = false
|
38
|
-
debugger.update_threads.each do |thread|
|
39
|
-
thread.switch
|
40
|
-
while thread.need_finish_frame
|
41
|
-
should_check_threads_state = true
|
42
|
-
thread.finish
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
debugger.wait_line_event
|
48
|
-
debugger.load_debugger
|
49
|
-
debugger.exit
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def get_child_pids(pid)
|
54
|
-
return [] unless command_exists 'pgrep'
|
55
|
-
|
56
|
-
pids = Array.new
|
57
|
-
|
58
|
-
q = Queue.new
|
59
|
-
q.push(pid)
|
60
|
-
|
61
|
-
until q.empty? do
|
62
|
-
pid = q.pop
|
63
|
-
|
64
|
-
pipe = IO.popen("pgrep -P #{pid}")
|
65
|
-
|
66
|
-
pipe.readlines.each do |child|
|
67
|
-
child_pid = child.strip.to_i
|
68
|
-
q.push(child_pid)
|
69
|
-
pids << child_pid
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
filter_ruby_processes(pids)
|
74
|
-
end
|
75
|
-
|
76
|
-
def filter_ruby_processes(pids)
|
77
|
-
pipe = IO.popen(%Q(lsof -c ruby | awk '{print $2 ":" $9}' | grep -E 'bin/ruby([[:digit:]]+\.?)*$'))
|
78
|
-
|
79
|
-
ruby_processes = Set.new
|
80
|
-
|
81
|
-
pipe.readlines.each do |process|
|
82
|
-
pid = process.split(/:/).first
|
83
|
-
ruby_processes.add(pid.to_i)
|
84
|
-
end
|
85
|
-
|
86
|
-
ruby_processes_pids, non_ruby_processes_pids = pids.partition {|pid| ruby_processes.include? pid}
|
87
|
-
|
88
|
-
DebugPrinter.print_debug("The following child processes was added to attach: #{ruby_processes_pids.join(', ')}") unless ruby_processes_pids.empty?
|
89
|
-
DebugPrinter.print_debug("The following child are not ruby processes: #{non_ruby_processes_pids.join(', ')}") unless non_ruby_processes_pids.empty?
|
90
|
-
|
91
|
-
ruby_processes_pids
|
92
|
-
end
|
93
|
-
|
94
|
-
def command_exists(command)
|
95
|
-
checking_command = "checking command #{command} for existence\n"
|
96
|
-
`command -v #{command} >/dev/null 2>&1 || { exit 1; }`
|
97
|
-
if $?.exitstatus != 0
|
98
|
-
DebugPrinter.print_debug("#{checking_command}command does not exist.")
|
99
|
-
else
|
100
|
-
DebugPrinter.print_debug("#{checking_command}command does exist.")
|
101
|
-
end
|
102
|
-
$?.exitstatus == 0
|
103
|
-
end
|
104
|
-
|
105
|
-
def choose_debugger(ruby_path, pid, gems_to_include, debugger_loader_path, argv)
|
106
|
-
if command_exists(LLDB.to_s)
|
107
|
-
debugger = LLDB.new(ruby_path, pid, '--no-lldbinit', gems_to_include, debugger_loader_path, argv)
|
108
|
-
elsif command_exists(GDB.to_s)
|
109
|
-
debugger = GDB.new(ruby_path, pid, '-nh -nx', gems_to_include, debugger_loader_path, argv)
|
110
|
-
else
|
111
|
-
raise 'Neither gdb nor lldb was found. Aborting.'
|
112
|
-
end
|
113
|
-
|
114
|
-
debugger
|
1
|
+
require 'ruby-debug-ide/attach/lldb'
|
2
|
+
require 'ruby-debug-ide/attach/gdb'
|
3
|
+
require 'socket'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
def attach_and_return_thread(options, pid, debugger_loader_path, argv)
|
7
|
+
Thread.new(argv) do |argv|
|
8
|
+
|
9
|
+
debugger = choose_debugger(options.ruby_path, pid, options.gems_to_include, debugger_loader_path, argv)
|
10
|
+
|
11
|
+
trap('INT') do
|
12
|
+
unless debugger.exited?
|
13
|
+
$stderr.puts "backtraces for threads:\n\n"
|
14
|
+
process_threads = debugger.process_threads
|
15
|
+
if process_threads
|
16
|
+
process_threads.each do |thread|
|
17
|
+
$stderr.puts "#{thread.thread_info}\n#{thread.last_bt}\n\n"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
debugger.exit
|
21
|
+
end
|
22
|
+
exit!
|
23
|
+
end
|
24
|
+
|
25
|
+
debugger.attach_to_process
|
26
|
+
debugger.set_flags
|
27
|
+
|
28
|
+
if debugger.check_already_under_debug
|
29
|
+
$stderr.puts "Process #{debugger.pid} is already under debug"
|
30
|
+
debugger.exit
|
31
|
+
exit!
|
32
|
+
end
|
33
|
+
|
34
|
+
should_check_threads_state = true
|
35
|
+
|
36
|
+
while should_check_threads_state
|
37
|
+
should_check_threads_state = false
|
38
|
+
debugger.update_threads.each do |thread|
|
39
|
+
thread.switch
|
40
|
+
while thread.need_finish_frame
|
41
|
+
should_check_threads_state = true
|
42
|
+
thread.finish
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
debugger.wait_line_event
|
48
|
+
debugger.load_debugger
|
49
|
+
debugger.exit
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_child_pids(pid)
|
54
|
+
return [] unless command_exists 'pgrep'
|
55
|
+
|
56
|
+
pids = Array.new
|
57
|
+
|
58
|
+
q = Queue.new
|
59
|
+
q.push(pid)
|
60
|
+
|
61
|
+
until q.empty? do
|
62
|
+
pid = q.pop
|
63
|
+
|
64
|
+
pipe = IO.popen("pgrep -P #{pid}")
|
65
|
+
|
66
|
+
pipe.readlines.each do |child|
|
67
|
+
child_pid = child.strip.to_i
|
68
|
+
q.push(child_pid)
|
69
|
+
pids << child_pid
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
filter_ruby_processes(pids)
|
74
|
+
end
|
75
|
+
|
76
|
+
def filter_ruby_processes(pids)
|
77
|
+
pipe = IO.popen(%Q(lsof -c ruby | awk '{print $2 ":" $9}' | grep -E 'bin/ruby([[:digit:]]+\.?)*$'))
|
78
|
+
|
79
|
+
ruby_processes = Set.new
|
80
|
+
|
81
|
+
pipe.readlines.each do |process|
|
82
|
+
pid = process.split(/:/).first
|
83
|
+
ruby_processes.add(pid.to_i)
|
84
|
+
end
|
85
|
+
|
86
|
+
ruby_processes_pids, non_ruby_processes_pids = pids.partition {|pid| ruby_processes.include? pid}
|
87
|
+
|
88
|
+
DebugPrinter.print_debug("The following child processes was added to attach: #{ruby_processes_pids.join(', ')}") unless ruby_processes_pids.empty?
|
89
|
+
DebugPrinter.print_debug("The following child are not ruby processes: #{non_ruby_processes_pids.join(', ')}") unless non_ruby_processes_pids.empty?
|
90
|
+
|
91
|
+
ruby_processes_pids
|
92
|
+
end
|
93
|
+
|
94
|
+
def command_exists(command)
|
95
|
+
checking_command = "checking command #{command} for existence\n"
|
96
|
+
`command -v #{command} >/dev/null 2>&1 || { exit 1; }`
|
97
|
+
if $?.exitstatus != 0
|
98
|
+
DebugPrinter.print_debug("#{checking_command}command does not exist.")
|
99
|
+
else
|
100
|
+
DebugPrinter.print_debug("#{checking_command}command does exist.")
|
101
|
+
end
|
102
|
+
$?.exitstatus == 0
|
103
|
+
end
|
104
|
+
|
105
|
+
def choose_debugger(ruby_path, pid, gems_to_include, debugger_loader_path, argv)
|
106
|
+
if command_exists(LLDB.to_s)
|
107
|
+
debugger = LLDB.new(ruby_path, pid, '--no-lldbinit', gems_to_include, debugger_loader_path, argv)
|
108
|
+
elsif command_exists(GDB.to_s)
|
109
|
+
debugger = GDB.new(ruby_path, pid, '-nh -nx', gems_to_include, debugger_loader_path, argv)
|
110
|
+
else
|
111
|
+
raise 'Neither gdb nor lldb was found. Aborting.'
|
112
|
+
end
|
113
|
+
|
114
|
+
debugger
|
115
115
|
end
|
@@ -1,187 +1,187 @@
|
|
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/thread_alias'
|
8
|
-
require 'ruby-debug-ide/helper'
|
9
|
-
require 'delegate'
|
10
|
-
|
11
|
-
module Debugger
|
12
|
-
|
13
|
-
class Command < SimpleDelegator # :nodoc:
|
14
|
-
SubcmdStruct=Struct.new(:name, :min, :short_help, :long_help) unless
|
15
|
-
defined?(SubcmdStruct)
|
16
|
-
|
17
|
-
# Find param in subcmds. param id downcased and can be abbreviated
|
18
|
-
# to the minimum length listed in the subcommands
|
19
|
-
def find(subcmds, param)
|
20
|
-
param.downcase!
|
21
|
-
for try_subcmd in subcmds do
|
22
|
-
if (param.size >= try_subcmd.min) and
|
23
|
-
(try_subcmd.name[0..param.size-1] == param)
|
24
|
-
return try_subcmd
|
25
|
-
end
|
26
|
-
end
|
27
|
-
return nil
|
28
|
-
end
|
29
|
-
|
30
|
-
class << self
|
31
|
-
def commands
|
32
|
-
@commands ||= []
|
33
|
-
end
|
34
|
-
|
35
|
-
DEF_OPTIONS = {
|
36
|
-
:event => true,
|
37
|
-
:control => false,
|
38
|
-
:unknown => false,
|
39
|
-
:need_context => false,
|
40
|
-
}
|
41
|
-
|
42
|
-
def inherited(klass)
|
43
|
-
DEF_OPTIONS.each do |o, v|
|
44
|
-
klass.options[o] = v if klass.options[o].nil?
|
45
|
-
end
|
46
|
-
commands << klass
|
47
|
-
end
|
48
|
-
|
49
|
-
def load_commands
|
50
|
-
dir = File.dirname(__FILE__)
|
51
|
-
Dir[File.join(dir, 'commands', '*')].each do |file|
|
52
|
-
require file if file =~ /\.rb$/
|
53
|
-
end
|
54
|
-
Debugger.constants.grep(/Functions$/).map { |name| Debugger.const_get(name) }.each do |mod|
|
55
|
-
include mod
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def method_missing(meth, *args, &block)
|
60
|
-
if meth.to_s =~ /^(.+?)=$/
|
61
|
-
@options[$1.intern] = args.first
|
62
|
-
else
|
63
|
-
if @options.has_key?(meth)
|
64
|
-
@options[meth]
|
65
|
-
else
|
66
|
-
super
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def options
|
72
|
-
@options ||= {}
|
73
|
-
end
|
74
|
-
|
75
|
-
def unescape_incoming(str)
|
76
|
-
str.gsub(/((?:^|[^\\])(?:\\\\)*)((?:\\n)+)/) do |_|
|
77
|
-
$1 + "\n" * ($2.size / 2)
|
78
|
-
end.gsub(/\\\\/, '\\')
|
79
|
-
end
|
80
|
-
|
81
|
-
def file_filter_supported?
|
82
|
-
defined?(Debugger.file_filter)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def initialize(state, printer)
|
87
|
-
@state, @printer = state, printer
|
88
|
-
super @printer
|
89
|
-
end
|
90
|
-
|
91
|
-
def match(input)
|
92
|
-
@match = regexp.match(input)
|
93
|
-
end
|
94
|
-
|
95
|
-
protected
|
96
|
-
|
97
|
-
def errmsg(*args)
|
98
|
-
@printer.print_error(*args)
|
99
|
-
end
|
100
|
-
|
101
|
-
def print(*args)
|
102
|
-
@state.print(*args)
|
103
|
-
end
|
104
|
-
|
105
|
-
# see Timeout::timeout, the difference is that we must use a DebugThread
|
106
|
-
# because every other thread would be halted when the event hook is reached
|
107
|
-
# in ruby-debug.c
|
108
|
-
def timeout(sec)
|
109
|
-
return yield if sec == nil or sec.zero?
|
110
|
-
if Thread.respond_to?(:critical) and Thread.critical
|
111
|
-
raise ThreadError, "timeout within critical session"
|
112
|
-
end
|
113
|
-
begin
|
114
|
-
x = Thread.current
|
115
|
-
y = DebugThread.start {
|
116
|
-
sleep sec
|
117
|
-
x.raise StandardError, "Timeout: evaluation took longer than #{sec} seconds." if x.alive?
|
118
|
-
}
|
119
|
-
yield sec
|
120
|
-
ensure
|
121
|
-
y.kill if y and y.alive?
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def debug_eval(str, b = get_binding)
|
126
|
-
begin
|
127
|
-
str = str.to_s
|
128
|
-
str.force_encoding('UTF-8') if(RUBY_VERSION >= '2.0')
|
129
|
-
to_inspect = Command.unescape_incoming(str)
|
130
|
-
max_time = Debugger.evaluation_timeout
|
131
|
-
@printer.print_debug("Evaluating %s with timeout after %i sec", str, max_time)
|
132
|
-
|
133
|
-
Debugger::TimeoutHandler.do_thread_alias
|
134
|
-
|
135
|
-
eval_result = nil
|
136
|
-
|
137
|
-
timeout(max_time) do
|
138
|
-
eval_result = eval(to_inspect, b)
|
139
|
-
end
|
140
|
-
|
141
|
-
Debugger::TimeoutHandler.undo_thread_alias
|
142
|
-
|
143
|
-
return eval_result
|
144
|
-
rescue StandardError, ScriptError => e
|
145
|
-
@printer.print_exception(e, @state.binding)
|
146
|
-
throw :debug_error
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def debug_silent_eval(str)
|
151
|
-
begin
|
152
|
-
str = str.to_s
|
153
|
-
eval(str, get_binding)
|
154
|
-
rescue StandardError, ScriptError
|
155
|
-
nil
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def get_binding
|
160
|
-
@state.context.frame_binding(@state.frame_pos)
|
161
|
-
end
|
162
|
-
|
163
|
-
def line_at(file, line)
|
164
|
-
Debugger.line_at(file, line)
|
165
|
-
end
|
166
|
-
|
167
|
-
def get_context(thnum)
|
168
|
-
Debugger.contexts.find{|c| c.thnum == thnum}
|
169
|
-
end
|
170
|
-
|
171
|
-
def realpath(filename)
|
172
|
-
is_dir = filename.end_with?(File::SEPARATOR)
|
173
|
-
if filename.index(File::SEPARATOR) || File::ALT_SEPARATOR && filename.index(File::ALT_SEPARATOR)
|
174
|
-
filename = File.expand_path(filename)
|
175
|
-
end
|
176
|
-
if (RUBY_VERSION < '1.9') || (RbConfig::CONFIG['host_os'] =~ /mswin/)
|
177
|
-
filename
|
178
|
-
else
|
179
|
-
filename = File.realpath(filename) rescue filename
|
180
|
-
filename = filename + File::SEPARATOR if is_dir && !filename.end_with?(File::SEPARATOR)
|
181
|
-
filename
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
Command.load_commands
|
187
|
-
end
|
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/thread_alias'
|
8
|
+
require 'ruby-debug-ide/helper'
|
9
|
+
require 'delegate'
|
10
|
+
|
11
|
+
module Debugger
|
12
|
+
|
13
|
+
class Command < SimpleDelegator # :nodoc:
|
14
|
+
SubcmdStruct=Struct.new(:name, :min, :short_help, :long_help) unless
|
15
|
+
defined?(SubcmdStruct)
|
16
|
+
|
17
|
+
# Find param in subcmds. param id downcased and can be abbreviated
|
18
|
+
# to the minimum length listed in the subcommands
|
19
|
+
def find(subcmds, param)
|
20
|
+
param.downcase!
|
21
|
+
for try_subcmd in subcmds do
|
22
|
+
if (param.size >= try_subcmd.min) and
|
23
|
+
(try_subcmd.name[0..param.size-1] == param)
|
24
|
+
return try_subcmd
|
25
|
+
end
|
26
|
+
end
|
27
|
+
return nil
|
28
|
+
end
|
29
|
+
|
30
|
+
class << self
|
31
|
+
def commands
|
32
|
+
@commands ||= []
|
33
|
+
end
|
34
|
+
|
35
|
+
DEF_OPTIONS = {
|
36
|
+
:event => true,
|
37
|
+
:control => false,
|
38
|
+
:unknown => false,
|
39
|
+
:need_context => false,
|
40
|
+
}
|
41
|
+
|
42
|
+
def inherited(klass)
|
43
|
+
DEF_OPTIONS.each do |o, v|
|
44
|
+
klass.options[o] = v if klass.options[o].nil?
|
45
|
+
end
|
46
|
+
commands << klass
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_commands
|
50
|
+
dir = File.dirname(__FILE__)
|
51
|
+
Dir[File.join(dir, 'commands', '*')].each do |file|
|
52
|
+
require file if file =~ /\.rb$/
|
53
|
+
end
|
54
|
+
Debugger.constants.grep(/Functions$/).map { |name| Debugger.const_get(name) }.each do |mod|
|
55
|
+
include mod
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def method_missing(meth, *args, &block)
|
60
|
+
if meth.to_s =~ /^(.+?)=$/
|
61
|
+
@options[$1.intern] = args.first
|
62
|
+
else
|
63
|
+
if @options.has_key?(meth)
|
64
|
+
@options[meth]
|
65
|
+
else
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def options
|
72
|
+
@options ||= {}
|
73
|
+
end
|
74
|
+
|
75
|
+
def unescape_incoming(str)
|
76
|
+
str.gsub(/((?:^|[^\\])(?:\\\\)*)((?:\\n)+)/) do |_|
|
77
|
+
$1 + "\n" * ($2.size / 2)
|
78
|
+
end.gsub(/\\\\/, '\\')
|
79
|
+
end
|
80
|
+
|
81
|
+
def file_filter_supported?
|
82
|
+
defined?(Debugger.file_filter)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def initialize(state, printer)
|
87
|
+
@state, @printer = state, printer
|
88
|
+
super @printer
|
89
|
+
end
|
90
|
+
|
91
|
+
def match(input)
|
92
|
+
@match = regexp.match(input)
|
93
|
+
end
|
94
|
+
|
95
|
+
protected
|
96
|
+
|
97
|
+
def errmsg(*args)
|
98
|
+
@printer.print_error(*args)
|
99
|
+
end
|
100
|
+
|
101
|
+
def print(*args)
|
102
|
+
@state.print(*args)
|
103
|
+
end
|
104
|
+
|
105
|
+
# see Timeout::timeout, the difference is that we must use a DebugThread
|
106
|
+
# because every other thread would be halted when the event hook is reached
|
107
|
+
# in ruby-debug.c
|
108
|
+
def timeout(sec)
|
109
|
+
return yield if sec == nil or sec.zero?
|
110
|
+
if Thread.respond_to?(:critical) and Thread.critical
|
111
|
+
raise ThreadError, "timeout within critical session"
|
112
|
+
end
|
113
|
+
begin
|
114
|
+
x = Thread.current
|
115
|
+
y = DebugThread.start {
|
116
|
+
sleep sec
|
117
|
+
x.raise StandardError, "Timeout: evaluation took longer than #{sec} seconds." if x.alive?
|
118
|
+
}
|
119
|
+
yield sec
|
120
|
+
ensure
|
121
|
+
y.kill if y and y.alive?
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def debug_eval(str, b = get_binding)
|
126
|
+
begin
|
127
|
+
str = str.to_s
|
128
|
+
str.force_encoding('UTF-8') if(RUBY_VERSION >= '2.0')
|
129
|
+
to_inspect = Command.unescape_incoming(str)
|
130
|
+
max_time = Debugger.evaluation_timeout
|
131
|
+
@printer.print_debug("Evaluating %s with timeout after %i sec", str, max_time)
|
132
|
+
|
133
|
+
Debugger::TimeoutHandler.do_thread_alias
|
134
|
+
|
135
|
+
eval_result = nil
|
136
|
+
|
137
|
+
timeout(max_time) do
|
138
|
+
eval_result = eval(to_inspect, b)
|
139
|
+
end
|
140
|
+
|
141
|
+
Debugger::TimeoutHandler.undo_thread_alias
|
142
|
+
|
143
|
+
return eval_result
|
144
|
+
rescue StandardError, ScriptError => e
|
145
|
+
@printer.print_exception(e, @state.binding)
|
146
|
+
throw :debug_error
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def debug_silent_eval(str)
|
151
|
+
begin
|
152
|
+
str = str.to_s
|
153
|
+
eval(str, get_binding)
|
154
|
+
rescue StandardError, ScriptError
|
155
|
+
nil
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def get_binding
|
160
|
+
@state.context.frame_binding(@state.frame_pos)
|
161
|
+
end
|
162
|
+
|
163
|
+
def line_at(file, line)
|
164
|
+
Debugger.line_at(file, line)
|
165
|
+
end
|
166
|
+
|
167
|
+
def get_context(thnum)
|
168
|
+
Debugger.contexts.find{|c| c.thnum == thnum}
|
169
|
+
end
|
170
|
+
|
171
|
+
def realpath(filename)
|
172
|
+
is_dir = filename.end_with?(File::SEPARATOR)
|
173
|
+
if filename.index(File::SEPARATOR) || File::ALT_SEPARATOR && filename.index(File::ALT_SEPARATOR)
|
174
|
+
filename = File.expand_path(filename)
|
175
|
+
end
|
176
|
+
if (RUBY_VERSION < '1.9') || (RbConfig::CONFIG['host_os'] =~ /mswin/)
|
177
|
+
filename
|
178
|
+
else
|
179
|
+
filename = File.realpath(filename) rescue filename
|
180
|
+
filename = filename + File::SEPARATOR if is_dir && !filename.end_with?(File::SEPARATOR)
|
181
|
+
filename
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
Command.load_commands
|
187
|
+
end
|