debug 1.0.0.alpha1 → 1.0.0.beta5
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/.github/workflows/ruby.yml +34 -0
- data/.gitignore +4 -0
- data/CONTRIBUTING.md +145 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +20 -19
- data/README.md +398 -54
- data/Rakefile +21 -1
- data/TODO.md +33 -0
- data/debug.gemspec +7 -10
- data/exe/rdbg +39 -1
- data/ext/debug/debug.c +119 -0
- data/ext/debug/extconf.rb +2 -0
- data/ext/debug/iseq_collector.c +91 -0
- data/lib/debug.rb +1 -1
- data/lib/debug/breakpoint.rb +362 -34
- data/lib/debug/client.rb +53 -17
- data/lib/debug/color.rb +70 -0
- data/lib/debug/config.rb +169 -7
- data/lib/debug/console.rb +96 -0
- data/lib/debug/frame_info.rb +144 -0
- data/lib/debug/open.rb +12 -0
- data/lib/debug/run.rb +4 -0
- data/lib/debug/server.rb +178 -27
- data/lib/debug/server_dap.rb +605 -0
- data/lib/debug/session.rb +779 -193
- data/lib/debug/source_repository.rb +77 -20
- data/lib/debug/test_console.rb +0 -0
- data/lib/debug/thread_client.rb +390 -179
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +350 -0
- metadata +29 -28
- data/lib/debug/repl.rb +0 -69
- data/lib/debug/tcpserver.rb +0 -22
- data/lib/debug/unixserver.rb +0 -18
data/lib/debug/client.rb
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
require 'socket'
|
|
2
|
+
require 'io/console/size'
|
|
3
|
+
|
|
2
4
|
require_relative 'config'
|
|
5
|
+
require_relative 'version'
|
|
6
|
+
|
|
7
|
+
# $VERBOSE = true
|
|
3
8
|
|
|
4
9
|
module DEBUGGER__
|
|
10
|
+
class CommandLineOptionError < Exception; end
|
|
11
|
+
|
|
5
12
|
class Client
|
|
6
13
|
begin
|
|
7
14
|
require 'readline'
|
|
@@ -15,15 +22,9 @@ module DEBUGGER__
|
|
|
15
22
|
end
|
|
16
23
|
end
|
|
17
24
|
|
|
18
|
-
def help
|
|
19
|
-
puts "-run -e attach # connect via UNIX Domain socket"
|
|
20
|
-
puts "-run -e attach name # connect via UNIX Domain socket with specified name"
|
|
21
|
-
puts "-run -e attach port_num # connect via TCP/IP socket with specified port"
|
|
22
|
-
puts "-run -e attach host port_num"
|
|
23
|
-
puts " # connect via TCP/IP socket with specified host and port"
|
|
24
|
-
end
|
|
25
|
-
|
|
26
25
|
def initialize argv
|
|
26
|
+
return util(argv) if String === argv
|
|
27
|
+
|
|
27
28
|
case argv.size
|
|
28
29
|
when 0
|
|
29
30
|
connect_unix
|
|
@@ -40,8 +41,24 @@ module DEBUGGER__
|
|
|
40
41
|
when 2
|
|
41
42
|
connect_tcp argv[0], argv[1]
|
|
42
43
|
else
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
raise CommandLineOptionError
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
@width = IO.console_size[1]
|
|
48
|
+
@width_changed = false
|
|
49
|
+
|
|
50
|
+
send "version: #{VERSION} width: #{@width} cookie: #{CONFIG[:cookie]}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def util name
|
|
54
|
+
case name
|
|
55
|
+
when 'gen-sockpath'
|
|
56
|
+
puts DEBUGGER__.create_unix_domain_socket_name
|
|
57
|
+
when 'list-socks'
|
|
58
|
+
cleanup_unix_domain_sockets
|
|
59
|
+
puts list_connections
|
|
60
|
+
else
|
|
61
|
+
raise "Unknown utility: #{name}"
|
|
45
62
|
end
|
|
46
63
|
end
|
|
47
64
|
|
|
@@ -57,19 +74,23 @@ module DEBUGGER__
|
|
|
57
74
|
end
|
|
58
75
|
end
|
|
59
76
|
|
|
77
|
+
def list_connections
|
|
78
|
+
Dir.glob(DEBUGGER__.create_unix_domain_socket_name_prefix + '*')
|
|
79
|
+
end
|
|
80
|
+
|
|
60
81
|
def connect_unix name = nil
|
|
61
82
|
if name
|
|
62
83
|
if File.exist? name
|
|
63
84
|
@s = Socket.unix(name)
|
|
64
85
|
else
|
|
65
|
-
@s = Socket.unix(File.join(DEBUGGER__.
|
|
86
|
+
@s = Socket.unix(File.join(DEBUGGER__.unix_domain_socket_dir, name))
|
|
66
87
|
end
|
|
67
88
|
else
|
|
68
89
|
cleanup_unix_domain_sockets
|
|
69
|
-
files =
|
|
90
|
+
files = list_connections
|
|
70
91
|
case files.size
|
|
71
92
|
when 0
|
|
72
|
-
$stderr.puts "
|
|
93
|
+
$stderr.puts "No debug session is available."
|
|
73
94
|
exit
|
|
74
95
|
when 1
|
|
75
96
|
@s = Socket.unix(files.first)
|
|
@@ -87,13 +108,22 @@ module DEBUGGER__
|
|
|
87
108
|
@s = Socket.tcp(host, port)
|
|
88
109
|
end
|
|
89
110
|
|
|
111
|
+
def send msg
|
|
112
|
+
p send: msg if $VERBOSE
|
|
113
|
+
@s.puts msg
|
|
114
|
+
end
|
|
115
|
+
|
|
90
116
|
def connect
|
|
91
117
|
trap(:SIGINT){
|
|
92
|
-
|
|
118
|
+
send "pause"
|
|
119
|
+
}
|
|
120
|
+
trap(:SIGWINCH){
|
|
121
|
+
@width = IO.console_size[1]
|
|
122
|
+
@width_changed = true
|
|
93
123
|
}
|
|
94
124
|
|
|
95
125
|
while line = @s.gets
|
|
96
|
-
|
|
126
|
+
p recv: line if $VERBOSE
|
|
97
127
|
case line
|
|
98
128
|
when /^out (.*)/
|
|
99
129
|
puts "#{$1}"
|
|
@@ -109,10 +139,16 @@ module DEBUGGER__
|
|
|
109
139
|
end
|
|
110
140
|
|
|
111
141
|
line = (line || 'quit').strip
|
|
112
|
-
|
|
142
|
+
|
|
143
|
+
if @width_changed
|
|
144
|
+
@width_changed = false
|
|
145
|
+
send "width #{@width}"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
send "command #{line}"
|
|
113
149
|
when /^ask (.*)/
|
|
114
150
|
print $1
|
|
115
|
-
|
|
151
|
+
send "answer #{gets || ''}"
|
|
116
152
|
when /^quit/
|
|
117
153
|
raise 'quit'
|
|
118
154
|
else
|
data/lib/debug/color.rb
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'irb/color'
|
|
3
|
+
require "irb/color_printer"
|
|
4
|
+
rescue LoadError
|
|
5
|
+
warn "DEBUGGER: can not load newer irb for coloring. Write 'gem \"debug\" in your Gemfile."
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module DEBUGGER__
|
|
9
|
+
module Color
|
|
10
|
+
if defined? IRB::Color.colorize
|
|
11
|
+
def colorize str, color
|
|
12
|
+
if CONFIG[:use_colorize]
|
|
13
|
+
IRB::Color.colorize str, color
|
|
14
|
+
else
|
|
15
|
+
str
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
def colorize str, color
|
|
20
|
+
str
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if defined? IRB::ColorPrinter.pp
|
|
25
|
+
def color_pp obj
|
|
26
|
+
IRB::ColorPrinter.pp(obj, "")
|
|
27
|
+
end
|
|
28
|
+
else
|
|
29
|
+
def color_pp obj
|
|
30
|
+
obj.pretty_inspect
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def colored_inspect obj
|
|
35
|
+
if CONFIG[:use_colorize]
|
|
36
|
+
color_pp obj
|
|
37
|
+
else
|
|
38
|
+
obj.pretty_inspect
|
|
39
|
+
end
|
|
40
|
+
rescue => ex
|
|
41
|
+
err_msg = "#{ex.inspect} rescued during inspection"
|
|
42
|
+
string_result = obj.to_s rescue nil
|
|
43
|
+
|
|
44
|
+
# don't colorize the string here because it's not from user's application
|
|
45
|
+
if string_result
|
|
46
|
+
%Q{"#{string_result}" from #to_s because #{err_msg}}
|
|
47
|
+
else
|
|
48
|
+
err_msg
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if defined? IRB::Color.colorize_code
|
|
53
|
+
def colorize_code code
|
|
54
|
+
IRB::Color.colorize_code(code)
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
def colorize_code code
|
|
58
|
+
code
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def colorize_cyan(str)
|
|
63
|
+
colorize(str, [:CYAN, :BOLD])
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def colorize_blue(str)
|
|
67
|
+
colorize(str, [:BLUE, :BOLD])
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
data/lib/debug/config.rb
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
|
|
1
2
|
module DEBUGGER__
|
|
2
|
-
def self.
|
|
3
|
+
def self.unix_domain_socket_dir
|
|
3
4
|
case
|
|
4
|
-
when path =
|
|
5
|
+
when path = ::DEBUGGER__::CONFIG[:sock_dir]
|
|
5
6
|
when path = ENV['XDG_RUNTIME_DIR']
|
|
6
7
|
when home = ENV['HOME']
|
|
7
8
|
path = File.join(home, '.ruby-debug-sock')
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
case
|
|
11
|
+
when !File.exist?(path)
|
|
9
12
|
Dir.mkdir(path, 0700)
|
|
13
|
+
when !File.directory?(path)
|
|
14
|
+
raise "#{path} is not a directory."
|
|
10
15
|
end
|
|
11
16
|
else
|
|
12
17
|
raise 'specify RUBY_DEBUG_SOCK_DIR environment variable for UNIX domain socket directory.'
|
|
@@ -15,12 +20,169 @@ module DEBUGGER__
|
|
|
15
20
|
path
|
|
16
21
|
end
|
|
17
22
|
|
|
18
|
-
def self.create_unix_domain_socket_name_prefix
|
|
23
|
+
def self.create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir)
|
|
19
24
|
user = ENV['USER'] || 'ruby-debug'
|
|
20
|
-
File.join(
|
|
25
|
+
File.join(base_dir, "ruby-debug-#{user}")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.create_unix_domain_socket_name(base_dir = unix_domain_socket_dir)
|
|
29
|
+
create_unix_domain_socket_name_prefix(base_dir) + "-#{Process.pid}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
CONFIG_MAP = {
|
|
33
|
+
# boot setting
|
|
34
|
+
nonstop: 'RUBY_DEBUG_NONSTOP', # Nonstop mode ('1' is nonstop)
|
|
35
|
+
init_script: 'RUBY_DEBUG_INIT_SCRIPT', # debug command script path loaded at first stop
|
|
36
|
+
commands: 'RUBY_DEBUG_COMMANDS', # debug commands invoked at first stop. commands should be separated by ';;'
|
|
37
|
+
|
|
38
|
+
# UI setting
|
|
39
|
+
show_src_lines: 'RUBY_DEBUG_SHOW_SRC_LINES', # Show n lines source code on breakpoint (default: 10 lines).
|
|
40
|
+
show_frames: 'RUBY_DEBUG_SHOW_FRAMES', # Show n frames on breakpoint (default: 2 frames).
|
|
41
|
+
use_short_path: 'RUBY_DEBUG_USE_SHORT_PATH', # Show shoten PATH (like $(Gem)/foo.rb).
|
|
42
|
+
skip_nosrc: 'RUBY_DEBUG_SKIP_NOSRC', # Skip on no source code lines (default: false).
|
|
43
|
+
use_colorize: 'RUBY_DEBUG_USE_COLORIZE', # Show coloring
|
|
44
|
+
|
|
45
|
+
# remote
|
|
46
|
+
port: 'RUBY_DEBUG_PORT', # TCP/IP remote debugging: port
|
|
47
|
+
host: 'RUBY_DEBUG_HOST', # TCP/IP remote debugging: host (localhost if not given)
|
|
48
|
+
sock_path: 'RUBY_DEBUG_SOCK_PATH', # UNIX Domain Socket remote debugging: socket path
|
|
49
|
+
sock_dir: 'RUBY_DEBUG_SOCK_DIR', # UNIX Domain Socket remote debugging: socket directory
|
|
50
|
+
cookie: 'RUBY_DEBUG_COOKIE', # Cookie for negotiation
|
|
51
|
+
}.freeze
|
|
52
|
+
|
|
53
|
+
def self.config_to_env config
|
|
54
|
+
CONFIG_MAP.each{|key, evname|
|
|
55
|
+
ENV[evname] = config[key].to_s if config[key]
|
|
56
|
+
}
|
|
21
57
|
end
|
|
22
58
|
|
|
23
|
-
def self.
|
|
24
|
-
|
|
59
|
+
def self.parse_argv argv
|
|
60
|
+
config = {
|
|
61
|
+
mode: :start,
|
|
62
|
+
use_colorize: true,
|
|
63
|
+
}
|
|
64
|
+
CONFIG_MAP.each{|key, evname|
|
|
65
|
+
if val = ENV[evname]
|
|
66
|
+
if /_USE_/ =~ evname || /NONSTOP/ =~ evname
|
|
67
|
+
case val
|
|
68
|
+
when '1', 'true', 'TRUE', 'T'
|
|
69
|
+
config[key] = true
|
|
70
|
+
else
|
|
71
|
+
config[key] = false
|
|
72
|
+
end
|
|
73
|
+
else
|
|
74
|
+
config[key] = val
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
}
|
|
78
|
+
return config if !argv || argv.empty?
|
|
79
|
+
|
|
80
|
+
require 'optparse'
|
|
81
|
+
require_relative 'version'
|
|
82
|
+
|
|
83
|
+
opt = OptionParser.new do |o|
|
|
84
|
+
o.banner = "#{$0} [options] -- [debuggee options]"
|
|
85
|
+
o.separator ''
|
|
86
|
+
o.version = ::DEBUGGER__::VERSION
|
|
87
|
+
|
|
88
|
+
o.separator 'Debug console mode:'
|
|
89
|
+
o.on('-n', '--nonstop', 'Do not stop at the beginning of the script.') do
|
|
90
|
+
config[:nonstop] = '1'
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
o.on('-e COMMAND', 'execute debug command at the beginning of the script.') do |cmd|
|
|
94
|
+
config[:commands] ||= ''
|
|
95
|
+
config[:commands] << cmd + ';;'
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
o.on('-x FILE', '--init-script=FILE', 'execute debug command in the FILE.') do |file|
|
|
99
|
+
config[:init_script] = file
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
o.separator ''
|
|
103
|
+
|
|
104
|
+
o.on('-O', '--open', 'Start remote debugging with opening the network port.',
|
|
105
|
+
'If TCP/IP options are not given,',
|
|
106
|
+
'a UNIX domain socket will be used.') do
|
|
107
|
+
config[:remote] = true
|
|
108
|
+
end
|
|
109
|
+
o.on('--sock-path=SOCK_PATH', 'UNIX Doman socket path') do |path|
|
|
110
|
+
config[:sock_path] = path
|
|
111
|
+
end
|
|
112
|
+
o.on('--port=PORT', 'Listening TCP/IP port') do |port|
|
|
113
|
+
config[:port] = port
|
|
114
|
+
end
|
|
115
|
+
o.on('--host=HOST', 'Listening TCP/IP host') do |host|
|
|
116
|
+
config[:host] = host
|
|
117
|
+
end
|
|
118
|
+
o.on('--cookie=COOKIE', 'Set a cookie for connection') do |c|
|
|
119
|
+
config[:cookie] = c
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
rdbg = 'rdbg'
|
|
123
|
+
|
|
124
|
+
o.separator ''
|
|
125
|
+
o.separator ' Debug console mode runs Ruby program with the debug console.'
|
|
126
|
+
o.separator ''
|
|
127
|
+
o.separator " '#{rdbg} target.rb foo bar' starts like 'ruby target.rb foo bar'."
|
|
128
|
+
o.separator " '#{rdbg} -- -r foo -e bar' starts like 'ruby -r foo -e bar'."
|
|
129
|
+
o.separator " '#{rdbg} -O target.rb foo bar' starts and accepts attaching with UNIX domain socket."
|
|
130
|
+
o.separator " '#{rdbg} -O --port 1234 target.rb foo bar' starts accepts attaching with TCP/IP localhost:1234."
|
|
131
|
+
o.separator " '#{rdbg} -O --port 1234 -- -r foo -e bar' starts accepts attaching with TCP/IP localhost:1234."
|
|
132
|
+
|
|
133
|
+
o.separator ''
|
|
134
|
+
o.separator 'Attach mode:'
|
|
135
|
+
o.on('-A', '--attach', 'Attach to debuggee process.') do
|
|
136
|
+
config[:mode] = :attach
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
o.separator ''
|
|
140
|
+
o.separator ' Attach mode attaches the remote debug console to the debuggee process.'
|
|
141
|
+
o.separator ''
|
|
142
|
+
o.separator " '#{rdbg} -A' tries to connect via UNIX domain socket."
|
|
143
|
+
o.separator " #{' ' * rdbg.size} If there are multiple processes are waiting for the"
|
|
144
|
+
o.separator " #{' ' * rdbg.size} debugger connection, list possible debuggee names."
|
|
145
|
+
o.separator " '#{rdbg} -A path' tries to connect via UNIX domain socket with given path name."
|
|
146
|
+
o.separator " '#{rdbg} -A port' tries to connect to localhost:port via TCP/IP."
|
|
147
|
+
o.separator " '#{rdbg} -A host port' tries to connect to host:port via TCP/IP."
|
|
148
|
+
|
|
149
|
+
o.separator ''
|
|
150
|
+
o.separator 'Other options:'
|
|
151
|
+
|
|
152
|
+
o.on("-h", "--help", "Print help") do
|
|
153
|
+
puts o
|
|
154
|
+
exit
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
o.on('-c', '--command', 'Command mode (first argument is command name)') do
|
|
158
|
+
config[:command] = true
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
o.on('--util=NAME', 'Utility mode (used by tools)') do |name|
|
|
162
|
+
Client.new(name)
|
|
163
|
+
exit
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
o.separator ''
|
|
167
|
+
o.separator 'NOTE'
|
|
168
|
+
o.separator ' All messages communicated between a debugger and a debuggee are *NOT* encrypted.'
|
|
169
|
+
o.separator ' Please use the remote debugging feature carefully.'
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
opt.parse!(argv)
|
|
173
|
+
|
|
174
|
+
config
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
CONFIG = ::DEBUGGER__.parse_argv(ENV['RUBY_DEBUG_OPT'])
|
|
178
|
+
|
|
179
|
+
def self.set_config kw
|
|
180
|
+
kw.each{|k, v|
|
|
181
|
+
if CONFIG_MAP[k]
|
|
182
|
+
CONFIG[k] = v # TODO: ractor support
|
|
183
|
+
else
|
|
184
|
+
raise "unknown option: #{k}"
|
|
185
|
+
end
|
|
186
|
+
}
|
|
25
187
|
end
|
|
26
188
|
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require_relative 'session'
|
|
2
|
+
return unless defined?(DEBUGGER__)
|
|
3
|
+
|
|
4
|
+
require 'io/console/size'
|
|
5
|
+
|
|
6
|
+
module DEBUGGER__
|
|
7
|
+
class UI_Console < UI_Base
|
|
8
|
+
def initialize
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def remote?
|
|
12
|
+
false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def width
|
|
16
|
+
IO.console_size[1]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def quit n
|
|
20
|
+
exit n
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def ask prompt
|
|
24
|
+
setup_interrupt do
|
|
25
|
+
print prompt
|
|
26
|
+
(gets || '').strip
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def puts str = nil
|
|
31
|
+
case str
|
|
32
|
+
when Array
|
|
33
|
+
str.each{|line|
|
|
34
|
+
$stdout.puts line.chomp
|
|
35
|
+
}
|
|
36
|
+
when String
|
|
37
|
+
str.each_line{|line|
|
|
38
|
+
$stdout.puts line.chomp
|
|
39
|
+
}
|
|
40
|
+
when nil
|
|
41
|
+
$stdout.puts
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
begin
|
|
46
|
+
require 'readline'
|
|
47
|
+
|
|
48
|
+
def readline_setup
|
|
49
|
+
Readline.completion_proc = proc{|given|
|
|
50
|
+
buff = Readline.line_buffer
|
|
51
|
+
Readline.completion_append_character= ' '
|
|
52
|
+
|
|
53
|
+
if /\s/ =~ buff # second parameters
|
|
54
|
+
given = File.expand_path(given + 'a').sub(/a\z/, '')
|
|
55
|
+
files = Dir.glob(given + '*')
|
|
56
|
+
if files.size == 1 && File.directory?(files.first)
|
|
57
|
+
Readline.completion_append_character= '/'
|
|
58
|
+
end
|
|
59
|
+
files
|
|
60
|
+
else
|
|
61
|
+
DEBUGGER__.commands.grep(/\A#{given}/)
|
|
62
|
+
end
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def readline_body
|
|
67
|
+
readline_setup
|
|
68
|
+
Readline.readline("\n(rdbg) ", true)
|
|
69
|
+
end
|
|
70
|
+
rescue LoadError
|
|
71
|
+
def readline_body
|
|
72
|
+
print "\n(rdbg) "
|
|
73
|
+
gets
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def readline
|
|
78
|
+
setup_interrupt do
|
|
79
|
+
(readline_body || 'quit').strip
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def setup_interrupt
|
|
84
|
+
current_thread = Thread.current # should be session_server thread
|
|
85
|
+
|
|
86
|
+
prev_handler = trap(:INT){
|
|
87
|
+
current_thread.raise Interrupt
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
yield
|
|
91
|
+
ensure
|
|
92
|
+
trap(:INT, prev_handler)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|