debug 1.8.0 → 1.11.0

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.
data/debug.gemspec CHANGED
@@ -10,10 +10,11 @@ Gem::Specification.new do |spec|
10
10
  spec.description = %q{Debugging functionality for Ruby. This is completely rewritten debug.rb which was contained by the ancient Ruby versions.}
11
11
  spec.homepage = "https://github.com/ruby/debug"
12
12
  spec.licenses = ["Ruby", "BSD-2-Clause"]
13
- spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
14
14
 
15
15
  spec.metadata["homepage_uri"] = spec.homepage
16
16
  spec.metadata["source_code_uri"] = spec.homepage
17
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/releases/tag/v#{spec.version}"
17
18
 
18
19
  # Specify which files should be added to the gem when it is released.
19
20
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -27,6 +28,6 @@ Gem::Specification.new do |spec|
27
28
  spec.require_paths = ["lib"]
28
29
  spec.extensions = ['ext/debug/extconf.rb']
29
30
 
30
- spec.add_dependency "irb", ">= 1.5.0" # for binding.irb(show_code: false)
31
- spec.add_dependency "reline", ">= 0.3.1"
31
+ spec.add_dependency "irb", "~> 1.10" # for irb:debug integration
32
+ spec.add_dependency "reline", ">= 0.3.8"
32
33
  end
data/ext/debug/debug.c CHANGED
@@ -8,13 +8,13 @@ static VALUE rb_mDebugger;
8
8
 
9
9
  // iseq
10
10
  typedef struct rb_iseq_struct rb_iseq_t;
11
+ const rb_iseq_t *rb_iseqw_to_iseq(VALUE iseqw);
11
12
  VALUE rb_iseq_realpath(const rb_iseq_t *iseq);
12
13
 
13
14
  static VALUE
14
15
  iseq_realpath(VALUE iseqw)
15
16
  {
16
- rb_iseq_t *iseq = DATA_PTR(iseqw);
17
- return rb_iseq_realpath(iseq);
17
+ return rb_iseq_realpath(rb_iseqw_to_iseq(iseqw));
18
18
  }
19
19
 
20
20
  static VALUE rb_cFrameInfo;
@@ -62,15 +62,23 @@ di_body(const rb_debug_inspector_t *dc, void *ptr)
62
62
  long i;
63
63
 
64
64
  for (i=1; i<len; i++) {
65
- VALUE loc, e;
65
+ VALUE e;
66
66
  VALUE iseq = rb_debug_inspector_frame_iseq_get(dc, i);
67
+ VALUE loc = RARRAY_AREF(locs, i);
68
+ VALUE path;
67
69
 
68
70
  if (!NIL_P(iseq)) {
69
- VALUE path = iseq_realpath(iseq);
70
- if (!NIL_P(path) && !NIL_P(skip_path_prefix) && str_start_with(path, skip_path_prefix)) continue;
71
+ path = iseq_realpath(iseq);
72
+ }
73
+ else {
74
+ // C frame
75
+ path = rb_funcall(loc, rb_intern("path"), 0);
76
+ }
77
+
78
+ if (!NIL_P(path) && !NIL_P(skip_path_prefix) && str_start_with(path, skip_path_prefix)) {
79
+ continue;
71
80
  }
72
81
 
73
- loc = RARRAY_AREF(locs, i);
74
82
  e = di_entry(loc,
75
83
  rb_debug_inspector_frame_self_get(dc, i),
76
84
  rb_debug_inspector_frame_binding_get(dc, i),
@@ -113,26 +121,26 @@ frame_depth(VALUE self)
113
121
 
114
122
  // iseq
115
123
 
116
- const struct rb_iseq *rb_iseqw_to_iseq(VALUE iseqw);
124
+ const rb_iseq_t *rb_iseqw_to_iseq(VALUE iseqw);
117
125
 
118
126
  #ifdef HAVE_RB_ISEQ_TYPE
119
- VALUE rb_iseq_type(const struct rb_iseq *);
127
+ VALUE rb_iseq_type(const rb_iseq_t *);
120
128
 
121
129
  static VALUE
122
130
  iseq_type(VALUE iseqw)
123
131
  {
124
- const struct rb_iseq *iseq = rb_iseqw_to_iseq(iseqw);
132
+ const rb_iseq_t *iseq = rb_iseqw_to_iseq(iseqw);
125
133
  return rb_iseq_type(iseq);
126
134
  }
127
135
  #endif
128
136
 
129
137
  #ifdef HAVE_RB_ISEQ_PARAMETERS
130
- VALUE rb_iseq_parameters(const struct rb_iseq *, int is_proc);
138
+ VALUE rb_iseq_parameters(const rb_iseq_t *, int is_proc);
131
139
 
132
140
  static VALUE
133
141
  iseq_parameters_symbols(VALUE iseqw)
134
142
  {
135
- const struct rb_iseq *iseq = rb_iseqw_to_iseq(iseqw);
143
+ const rb_iseq_t *iseq = rb_iseqw_to_iseq(iseqw);
136
144
  VALUE params = rb_iseq_parameters(iseq, 0);
137
145
  VALUE ary = rb_ary_new();
138
146
 
@@ -159,12 +167,12 @@ iseq_parameters_symbols(VALUE iseqw)
159
167
  #endif
160
168
 
161
169
  #ifdef HAVE_RB_ISEQ_CODE_LOCATION
162
- void rb_iseq_code_location(const struct rb_iseq *, int *first_lineno, int *first_column, int *last_lineno, int *last_column);
170
+ void rb_iseq_code_location(const rb_iseq_t *, int *first_lineno, int *first_column, int *last_lineno, int *last_column);
163
171
 
164
172
  static VALUE
165
173
  iseq_first_line(VALUE iseqw)
166
174
  {
167
- const struct rb_iseq *iseq = rb_iseqw_to_iseq(iseqw);
175
+ const rb_iseq_t *iseq = rb_iseqw_to_iseq(iseqw);
168
176
  int line;
169
177
  rb_iseq_code_location(iseq, &line, NULL, NULL, NULL);
170
178
  return INT2NUM(line);
@@ -173,20 +181,24 @@ iseq_first_line(VALUE iseqw)
173
181
  static VALUE
174
182
  iseq_last_line(VALUE iseqw)
175
183
  {
176
- const struct rb_iseq *iseq = rb_iseqw_to_iseq(iseqw);
184
+ const rb_iseq_t *iseq = rb_iseqw_to_iseq(iseqw);
177
185
  int line;
178
186
  rb_iseq_code_location(iseq, NULL, NULL, &line, NULL);
179
187
  return INT2NUM(line);
180
188
  }
181
189
  #endif
182
190
 
191
+ #ifdef HAVE_RB_ISEQ
183
192
  void Init_iseq_collector(void);
193
+ #endif
184
194
 
185
195
  void
186
196
  Init_debug(void)
187
197
  {
198
+ #ifdef HAVE_RB_ISEQ
188
199
  VALUE rb_mRubyVM = rb_const_get(rb_cObject, rb_intern("RubyVM"));
189
200
  VALUE rb_cISeq = rb_const_get(rb_mRubyVM, rb_intern("InstructionSequence"));
201
+ #endif
190
202
  rb_mDebugger = rb_const_get(rb_cObject, rb_intern("DEBUGGER__"));
191
203
  rb_cFrameInfo = rb_const_get(rb_mDebugger, rb_intern("FrameInfo"));
192
204
 
@@ -210,5 +222,7 @@ Init_debug(void)
210
222
  rb_define_method(rb_cISeq, "last_line", iseq_last_line, 0);
211
223
  #endif
212
224
 
225
+ #ifdef HAVE_RB_ISEQ
213
226
  Init_iseq_collector();
227
+ #endif
214
228
  }
data/ext/debug/extconf.rb CHANGED
@@ -4,6 +4,7 @@ File.write("debug_version.h", "#define RUBY_DEBUG_VERSION \"#{DEBUGGER__::VERSIO
4
4
  $distcleanfiles << "debug_version.h"
5
5
 
6
6
  if defined? RubyVM
7
+ $defs << '-DHAVE_RB_ISEQ'
7
8
  $defs << '-DHAVE_RB_ISEQ_PARAMETERS'
8
9
  $defs << '-DHAVE_RB_ISEQ_CODE_LOCATION'
9
10
 
@@ -1,5 +1,6 @@
1
1
  #include <ruby/ruby.h>
2
2
 
3
+ #ifdef HAVE_RB_ISEQ
3
4
  VALUE rb_iseqw_new(VALUE v);
4
5
  void rb_objspace_each_objects(
5
6
  int (*callback)(void *start, void *end, size_t stride, void *data),
@@ -89,3 +90,4 @@ Init_iseq_collector(void)
89
90
  rb_define_singleton_method(rb_mObjSpace, "each_iseq", each_iseq, 0);
90
91
  rb_define_singleton_method(rb_mObjSpace, "count_iseq", count_iseq, 0);
91
92
  }
93
+ #endif
data/lib/debug/client.rb CHANGED
@@ -165,15 +165,16 @@ module DEBUGGER__
165
165
  end
166
166
  else
167
167
  Client.cleanup_unix_domain_sockets
168
- files = Client.list_connections verbose: true
168
+ files = Client.list_connections
169
169
 
170
170
  case files.size
171
171
  when 0
172
172
  $stderr.puts "No debug session is available."
173
173
  exit
174
174
  when 1
175
- @s = Socket.unix(files.first.first)
175
+ @s = Socket.unix(files.first)
176
176
  else
177
+ files = Client.list_connections verbose: true
177
178
  $stderr.puts "Please select a debug session:"
178
179
  files.each{|(f, desc)|
179
180
  $stderr.puts " #{File.basename(f)} (#{desc})"
data/lib/debug/config.rb CHANGED
@@ -12,47 +12,52 @@ module DEBUGGER__
12
12
 
13
13
  CONFIG_SET = {
14
14
  # UI setting
15
- log_level: ['RUBY_DEBUG_LOG_LEVEL', "UI: Log level same as Logger", :loglevel, "WARN"],
16
- show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint", :int, "10"],
17
- show_evaledsrc: ['RUBY_DEBUG_SHOW_EVALEDSRC', "UI: Show actually evaluated source", :bool, "false"],
18
- show_frames: ['RUBY_DEBUG_SHOW_FRAMES', "UI: Show n frames on breakpoint", :int, "2"],
19
- use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shorten PATH (like $(Gem)/foo.rb)", :bool, "false"],
20
- no_color: ['RUBY_DEBUG_NO_COLOR', "UI: Do not use colorize", :bool, "false"],
21
- no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT", :bool, "false"],
22
- no_reline: ['RUBY_DEBUG_NO_RELINE', "UI: Do not use Reline library", :bool, "false"],
23
- no_hint: ['RUBY_DEBUG_NO_HINT', "UI: Do not show the hint on the REPL", :bool, "false"],
24
- no_lineno: ['RUBY_DEBUG_NO_LINENO', "UI: Do not show line numbers", :bool, "false"],
15
+ log_level: ['RUBY_DEBUG_LOG_LEVEL', "UI: Log level same as Logger", :loglevel, "WARN"],
16
+ show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint", :int, "10"],
17
+ show_src_lines_frame:['RUBY_DEBUG_SHOW_SRC_LINES_FRAME', "UI: Show n lines source code on frame operations", :int, "1"],
18
+ show_evaledsrc: ['RUBY_DEBUG_SHOW_EVALEDSRC', "UI: Show actually evaluated source", :bool, "false"],
19
+ show_frames: ['RUBY_DEBUG_SHOW_FRAMES', "UI: Show n frames on breakpoint", :int, "2"],
20
+ use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shorten PATH (like $(Gem)/foo.rb)", :bool, "false"],
21
+ no_color: ['RUBY_DEBUG_NO_COLOR', "UI: Do not use colorize", :bool, "false"],
22
+ no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT", :bool, "false"],
23
+ no_reline: ['RUBY_DEBUG_NO_RELINE', "UI: Do not use Reline library", :bool, "false"],
24
+ no_hint: ['RUBY_DEBUG_NO_HINT', "UI: Do not show the hint on the REPL", :bool, "false"],
25
+ no_lineno: ['RUBY_DEBUG_NO_LINENO', "UI: Do not show line numbers", :bool, "false"],
26
+ no_repeat: ['RUBY_DEBUG_NO_REPEAT', "UI: Do not repeat last line when empty line",:bool, "false"],
27
+ irb_console: ["RUBY_DEBUG_IRB_CONSOLE", "UI: Use IRB as the console", :bool, "false"],
25
28
 
26
29
  # control setting
27
- skip_path: ['RUBY_DEBUG_SKIP_PATH', "CONTROL: Skip showing/entering frames for given paths", :path],
28
- skip_nosrc: ['RUBY_DEBUG_SKIP_NOSRC', "CONTROL: Skip on no source code lines", :bool, "false"],
29
- keep_alloc_site:['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it", :bool, "false"],
30
- postmortem: ['RUBY_DEBUG_POSTMORTEM', "CONTROL: Enable postmortem debug", :bool, "false"],
31
- fork_mode: ['RUBY_DEBUG_FORK_MODE', "CONTROL: Control which process activates a debugger after fork (both/parent/child)", :forkmode, "both"],
32
- sigdump_sig: ['RUBY_DEBUG_SIGDUMP_SIG', "CONTROL: Sigdump signal", :bool, "false"],
30
+ skip_path: ['RUBY_DEBUG_SKIP_PATH', "CONTROL: Skip showing/entering frames for given paths", :path],
31
+ skip_nosrc: ['RUBY_DEBUG_SKIP_NOSRC', "CONTROL: Skip on no source code lines", :bool, "false"],
32
+ keep_alloc_site: ['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it", :bool, "false"],
33
+ postmortem: ['RUBY_DEBUG_POSTMORTEM', "CONTROL: Enable postmortem debug", :bool, "false"],
34
+ fork_mode: ['RUBY_DEBUG_FORK_MODE', "CONTROL: Control which process activates a debugger after fork (both/parent/child)", :forkmode, "both"],
35
+ sigdump_sig: ['RUBY_DEBUG_SIGDUMP_SIG', "CONTROL: Sigdump signal", :bool, "false"],
33
36
 
34
37
  # boot setting
35
- nonstop: ['RUBY_DEBUG_NONSTOP', "BOOT: Nonstop mode", :bool, "false"],
36
- stop_at_load: ['RUBY_DEBUG_STOP_AT_LOAD',"BOOT: Stop at just loading location", :bool, "false"],
37
- init_script: ['RUBY_DEBUG_INIT_SCRIPT', "BOOT: debug command script path loaded at first stop"],
38
- commands: ['RUBY_DEBUG_COMMANDS', "BOOT: debug commands invoked at first stop. Commands should be separated by `;;`"],
39
- no_rc: ['RUBY_DEBUG_NO_RC', "BOOT: ignore loading ~/.rdbgrc(.rb)", :bool, "false"],
40
- history_file: ['RUBY_DEBUG_HISTORY_FILE',"BOOT: history file", :string, "~/.rdbg_history"],
41
- save_history: ['RUBY_DEBUG_SAVE_HISTORY',"BOOT: maximum save history lines", :int, "10000"],
38
+ nonstop: ['RUBY_DEBUG_NONSTOP', "BOOT: Nonstop mode", :bool, "false"],
39
+ stop_at_load: ['RUBY_DEBUG_STOP_AT_LOAD',"BOOT: Stop at just loading location", :bool, "false"],
40
+ init_script: ['RUBY_DEBUG_INIT_SCRIPT', "BOOT: debug command script path loaded at first stop"],
41
+ commands: ['RUBY_DEBUG_COMMANDS', "BOOT: debug commands invoked at first stop. Commands should be separated by `;;`"],
42
+ no_rc: ['RUBY_DEBUG_NO_RC', "BOOT: ignore loading ~/.rdbgrc(.rb)", :bool, "false"],
43
+ history_file: ['RUBY_DEBUG_HISTORY_FILE',"BOOT: history file (default: ${XDG_STATE_HOME-~/.local/state}/rdbg/history)", :string, nil],
44
+ save_history: ['RUBY_DEBUG_SAVE_HISTORY',"BOOT: maximum save history lines", :int, "10000"],
42
45
 
43
46
  # remote setting
44
- open: ['RUBY_DEBUG_OPEN', "REMOTE: Open remote port (same as `rdbg --open` option)"],
45
- port: ['RUBY_DEBUG_PORT', "REMOTE: TCP/IP remote debugging: port"],
46
- host: ['RUBY_DEBUG_HOST', "REMOTE: TCP/IP remote debugging: host", :string, "127.0.0.1"],
47
- sock_path: ['RUBY_DEBUG_SOCK_PATH', "REMOTE: UNIX Domain Socket remote debugging: socket path"],
48
- sock_dir: ['RUBY_DEBUG_SOCK_DIR', "REMOTE: UNIX Domain Socket remote debugging: socket directory"],
49
- local_fs_map: ['RUBY_DEBUG_LOCAL_FS_MAP', "REMOTE: Specify local fs map", :path_map],
50
- skip_bp: ['RUBY_DEBUG_SKIP_BP', "REMOTE: Skip breakpoints if no clients are attached", :bool, 'false'],
51
- cookie: ['RUBY_DEBUG_COOKIE', "REMOTE: Cookie for negotiation"],
52
- chrome_path: ['RUBY_DEBUG_CHROME_PATH', "REMOTE: Platform dependent path of Chrome (For more information, See [here](https://github.com/ruby/debug/pull/334/files#diff-5fc3d0a901379a95bc111b86cf0090b03f857edfd0b99a0c1537e26735698453R55-R64))"],
47
+ open: ['RUBY_DEBUG_OPEN', "REMOTE: Open remote port (same as `rdbg --open` option)"],
48
+ port: ['RUBY_DEBUG_PORT', "REMOTE: TCP/IP remote debugging: port"],
49
+ port_range: ['RUBY_DEBUG_PORT_RANGE', "REMOTE: TCP/IP remote debugging: length of port range"],
50
+ host: ['RUBY_DEBUG_HOST', "REMOTE: TCP/IP remote debugging: host", :string, "127.0.0.1"],
51
+ sock_path: ['RUBY_DEBUG_SOCK_PATH', "REMOTE: UNIX Domain Socket remote debugging: socket path"],
52
+ sock_dir: ['RUBY_DEBUG_SOCK_DIR', "REMOTE: UNIX Domain Socket remote debugging: socket directory"],
53
+ local_fs_map: ['RUBY_DEBUG_LOCAL_FS_MAP', "REMOTE: Specify local fs map", :path_map],
54
+ skip_bp: ['RUBY_DEBUG_SKIP_BP', "REMOTE: Skip breakpoints if no clients are attached", :bool, 'false'],
55
+ cookie: ['RUBY_DEBUG_COOKIE', "REMOTE: Cookie for negotiation"],
56
+ session_name: ['RUBY_DEBUG_SESSION_NAME', "REMOTE: Session name for differentiating multiple sessions"],
57
+ chrome_path: ['RUBY_DEBUG_CHROME_PATH', "REMOTE: Platform dependent path of Chrome (For more information, See [here](https://github.com/ruby/debug/pull/334/files#diff-5fc3d0a901379a95bc111b86cf0090b03f857edfd0b99a0c1537e26735698453R55-R64))"],
53
58
 
54
59
  # obsolete
55
- parent_on_fork: ['RUBY_DEBUG_PARENT_ON_FORK', "OBSOLETE: Keep debugging parent process on fork", :bool, "false"],
60
+ parent_on_fork: ['RUBY_DEBUG_PARENT_ON_FORK', "OBSOLETE: Keep debugging parent process on fork", :bool, "false"],
56
61
  }.freeze
57
62
 
58
63
  CONFIG_MAP = CONFIG_SET.map{|k, (ev, _)| [k, ev]}.to_h.freeze
@@ -156,6 +161,22 @@ module DEBUGGER__
156
161
  SESSION.set_no_sigint_hook old, new
157
162
  end
158
163
  end
164
+
165
+ if_updated old_conf, conf, :irb_console do |old, new|
166
+ if defined?(SESSION) && SESSION.active?
167
+ # irb_console is switched from true to false
168
+ if old
169
+ SESSION.deactivate_irb_integration
170
+ # irb_console is switched from false to true
171
+ else
172
+ if CONFIG[:open]
173
+ SESSION.instance_variable_get(:@ui).puts "\nIRB is not supported on the remote console."
174
+ else
175
+ SESSION.activate_irb_integration
176
+ end
177
+ end
178
+ end
179
+ end
159
180
  end
160
181
 
161
182
  private def if_updated old_conf, new_conf, key
@@ -334,12 +355,18 @@ module DEBUGGER__
334
355
  o.on('--port=PORT', 'Listening TCP/IP port') do |port|
335
356
  config[:port] = port
336
357
  end
358
+ o.on('--port-range=PORT_RANGE', 'Number of ports to try to connect to') do |port_range|
359
+ config[:port_range] = port_range
360
+ end
337
361
  o.on('--host=HOST', 'Listening TCP/IP host') do |host|
338
362
  config[:host] = host
339
363
  end
340
364
  o.on('--cookie=COOKIE', 'Set a cookie for connection') do |c|
341
365
  config[:cookie] = c
342
366
  end
367
+ o.on('--session-name=NAME', 'Session name') do |name|
368
+ config[:session_name] = name
369
+ end
343
370
 
344
371
  rdbg = 'rdbg'
345
372
 
@@ -411,7 +438,6 @@ module DEBUGGER__
411
438
  if argv.empty?
412
439
  case
413
440
  when have_shown_version && config[:mode] == :start
414
- pp config
415
441
  exit
416
442
  end
417
443
  end
@@ -458,7 +484,7 @@ module DEBUGGER__
458
484
  require 'tmpdir'
459
485
 
460
486
  if tmpdir = Dir.tmpdir
461
- path = File.join(tmpdir, "ruby-debug-sock-#{Process.uid}")
487
+ path = File.join(tmpdir, "rdbg-#{Process.uid}")
462
488
 
463
489
  unless File.exist?(path)
464
490
  d = Dir.mktmpdir
@@ -471,7 +497,7 @@ module DEBUGGER__
471
497
 
472
498
  def self.unix_domain_socket_homedir
473
499
  if home = ENV['HOME']
474
- path = File.join(home, '.ruby-debug-sock')
500
+ path = File.join(home, '.rdbg-sock')
475
501
 
476
502
  unless File.exist?(path)
477
503
  Dir.mkdir(path, 0700)
@@ -495,12 +521,14 @@ module DEBUGGER__
495
521
  end
496
522
 
497
523
  def self.create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir)
498
- user = ENV['USER'] || 'UnknownUser'
499
- File.join(base_dir, "ruby-debug-#{user}")
524
+ File.join(base_dir, "rdbg")
500
525
  end
501
526
 
502
527
  def self.create_unix_domain_socket_name(base_dir = unix_domain_socket_dir)
503
- create_unix_domain_socket_name_prefix(base_dir) + "-#{Process.pid}"
528
+ suffix = "-#{Process.pid}"
529
+ name = CONFIG[:session_name]
530
+ suffix << "-#{name}" if name
531
+ create_unix_domain_socket_name_prefix(base_dir) + suffix
504
532
  end
505
533
 
506
534
  ## Help
data/lib/debug/console.rb CHANGED
@@ -5,30 +5,9 @@ module DEBUGGER__
5
5
  raise LoadError if CONFIG[:no_reline]
6
6
  require 'reline'
7
7
 
8
- # reline 0.2.7 or later is required.
9
- raise LoadError if Reline::VERSION < '0.2.7'
10
-
11
8
  require_relative 'color'
12
- include Color
13
-
14
- begin
15
- prev = trap(:SIGWINCH, nil)
16
- trap(:SIGWINCH, prev)
17
- SIGWINCH_SUPPORTED = true
18
- rescue ArgumentError
19
- SIGWINCH_SUPPORTED = false
20
- end
21
9
 
22
- # 0.2.7 has SIGWINCH issue on non-main thread
23
- class ::Reline::LineEditor
24
- m = Module.new do
25
- def reset(prompt = '', encoding:)
26
- super
27
- Signal.trap(:SIGWINCH, nil)
28
- end
29
- end
30
- prepend m
31
- end if SIGWINCH_SUPPORTED
10
+ include Color
32
11
 
33
12
  def parse_input buff, commands
34
13
  c, rest = get_command buff
@@ -56,10 +35,10 @@ module DEBUGGER__
56
35
  Reline.prompt_proc = -> args, *kw do
57
36
  case state = parse_input(args.first, commands)
58
37
  when nil, :command
59
- [prompt, prompt]
38
+ [prompt]
60
39
  when :ruby
61
- [prompt.sub('rdbg'){colorize('ruby', [:RED])}] * 2
62
- end
40
+ [prompt.sub('rdbg'){colorize('ruby', [:RED])}]
41
+ end * args.size
63
42
  end
64
43
 
65
44
  Reline.completion_proc = -> given do
@@ -96,7 +75,7 @@ module DEBUGGER__
96
75
  when nil
97
76
  buff
98
77
  when :ruby
99
- colorize_code(buff.chomp)
78
+ colorize_code(buff)
100
79
  end
101
80
  end unless CONFIG[:no_hint]
102
81
 
@@ -164,7 +143,7 @@ module DEBUGGER__
164
143
  rescue LoadError
165
144
  def readline prompt
166
145
  print prompt
167
- gets
146
+ $stdin.gets
168
147
  end
169
148
 
170
149
  def history
@@ -174,21 +153,28 @@ module DEBUGGER__
174
153
  end
175
154
 
176
155
  def history_file
177
- history_file = CONFIG[:history_file]
178
-
179
- if !history_file.empty?
180
- File.expand_path(history_file)
156
+ case
157
+ when (path = CONFIG[:history_file]) && !path.empty?
158
+ path = File.expand_path(path)
159
+ when (path = File.expand_path("~/.rdbg_history")) && File.exist?(path) # for compatibility
160
+ # path
181
161
  else
182
- history_file
162
+ state_dir = ENV['XDG_STATE_HOME'] || File.join(Dir.home, '.local', 'state')
163
+ path = File.join(File.expand_path(state_dir), 'rdbg', 'history')
183
164
  end
165
+
166
+ FileUtils.mkdir_p(File.dirname(path)) unless File.exist?(path)
167
+ path
184
168
  end
185
169
 
186
170
  FH = "# Today's OMIKUJI: "
187
171
 
188
172
  def read_history_file
189
- if history && File.exist?(path = history_file)
173
+ if history && File.exist?(path = history_file())
190
174
  f = (['', 'DAI-', 'CHU-', 'SHO-'].map{|e| e+'KICHI'}+['KYO']).sample
191
- ["#{FH}#{f}".dup] + File.readlines(path)
175
+ # Read history file and scrub invalid characters to prevent encoding errors
176
+ lines = File.readlines(path).map(&:scrub)
177
+ ["#{FH}#{f}".dup] + lines
192
178
  else
193
179
  []
194
180
  end
@@ -207,15 +193,17 @@ module DEBUGGER__
207
193
  def deactivate
208
194
  if history && @init_history_lines
209
195
  added_records = history.to_a[@init_history_lines .. -1]
210
- path = history_file
196
+ path = history_file()
211
197
  max = CONFIG[:save_history]
212
198
 
213
199
  if !added_records.empty? && !path.empty?
214
200
  orig_records = read_history_file
215
- open(history_file, 'w'){|f|
201
+ open(history_file(), 'w'){|f|
216
202
  (orig_records + added_records).last(max).each{|line|
217
- if !line.start_with?(FH) && !line.strip.empty?
218
- f.puts line.strip
203
+ # Use scrub to handle encoding issues gracefully
204
+ scrubbed_line = line.scrub.strip
205
+ if !line.start_with?(FH) && !scrubbed_line.empty?
206
+ f.puts scrubbed_line
219
207
  end
220
208
  }
221
209
  }
@@ -224,11 +212,13 @@ module DEBUGGER__
224
212
  end
225
213
 
226
214
  def load_history
227
- read_history_file.count{|line|
215
+ read_history_file.each{|line|
216
+ # Use scrub to handle encoding issues gracefully, then strip
217
+ line.scrub!
228
218
  line.strip!
229
219
  history << line unless line.empty?
230
- }
220
+ } if history.empty?
221
+ history.count
231
222
  end
232
223
  end # class Console
233
224
  end
234
-
@@ -86,7 +86,9 @@ module DEBUGGER__
86
86
 
87
87
  def block_identifier
88
88
  return unless frame_type == :block
89
- _, level, block_loc = location.label.match(BLOCK_LABL_REGEXP).to_a
89
+ re_match = location.label.match(BLOCK_LABL_REGEXP)
90
+ _, level, block_loc = re_match ? re_match.to_a : [nil, nil, location.label]
91
+
90
92
  [level || "", block_loc]
91
93
  end
92
94
 
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'irb'
4
+
5
+ module DEBUGGER__
6
+ module IrbPatch
7
+ def evaluate(line, line_no)
8
+ SESSION.send(:restart_all_threads)
9
+ super
10
+ # This is to communicate with the test framework so it can feed the next input
11
+ puts "INTERNAL_INFO: {}" if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal'
12
+ ensure
13
+ SESSION.send(:stop_all_threads)
14
+ end
15
+ end
16
+
17
+ class ThreadClient
18
+ def activate_irb_integration
19
+ IRB.setup(location, argv: [])
20
+ workspace = IRB::WorkSpace.new(current_frame&.binding || TOPLEVEL_BINDING)
21
+ irb = IRB::Irb.new(workspace)
22
+ IRB.conf[:MAIN_CONTEXT] = irb.context
23
+ IRB::Debug.setup(irb)
24
+ IRB::Context.prepend(IrbPatch)
25
+ end
26
+ end
27
+
28
+ class Session
29
+ def deactivate_irb_integration
30
+ Reline.completion_proc = nil
31
+ Reline.output_modifier_proc = nil
32
+ Reline.autocompletion = false
33
+ Reline.dig_perfect_match_proc = nil
34
+ reset_ui UI_LocalConsole.new
35
+ end
36
+ end
37
+ end
data/lib/debug/prelude.rb CHANGED
@@ -5,7 +5,7 @@ return if defined?(::DEBUGGER__::Session)
5
5
 
6
6
  # Put the following line in your login script (e.g. ~/.bash_profile) with modified path:
7
7
  #
8
- # export RUBYOPT="-r /path/to/debug/prelude $(RUBYOPT)"
8
+ # export RUBYOPT="-r /path/to/debug/prelude ${RUBYOPT}"
9
9
  #
10
10
  module Kernel
11
11
  def debugger(*a, up_level: 0, **kw)
data/lib/debug/server.rb CHANGED
@@ -133,7 +133,7 @@ module DEBUGGER__
133
133
  require 'etc'
134
134
 
135
135
  check_cookie $1
136
- @sock.puts "PID: #{Process.pid}, $0: #{$0}"
136
+ @sock.puts "PID: #{Process.pid}, $0: #{$0}, session_name: #{CONFIG[:session_name]}"
137
137
  @sock.puts "debug #{VERSION} on #{RUBY_DESCRIPTION}"
138
138
  @sock.puts "uname: #{Etc.uname.inspect}"
139
139
  @sock.close
@@ -149,7 +149,9 @@ module DEBUGGER__
149
149
  end
150
150
  parse_option(params)
151
151
 
152
- puts "DEBUGGER (client): Connected. PID:#{Process.pid}, $0:#{$0}"
152
+ session_name = CONFIG[:session_name]
153
+ session_name_str = ", session_name:#{session_name}" if session_name
154
+ puts "DEBUGGER (client): Connected. PID:#{Process.pid}, $0:#{$0}#{session_name_str}"
153
155
  puts "DEBUGGER (client): Type `Ctrl-C` to enter the debug console." unless @need_pause_at_first
154
156
  puts
155
157
 
@@ -397,6 +399,13 @@ module DEBUGGER__
397
399
  raise "Specify digits for port number"
398
400
  end
399
401
  end
402
+ @port_range = if @port.zero?
403
+ 0
404
+ else
405
+ port_range_str = (CONFIG[:port_range] || "0").to_s
406
+ raise "Specify a positive integer <=16 for port range" unless port_range_str.match?(/\A\d+\z/) && port_range_str.to_i <= 16
407
+ port_range_str.to_i
408
+ end
400
409
  @uuid = nil # for CDP
401
410
 
402
411
  super()
@@ -410,7 +419,7 @@ module DEBUGGER__
410
419
  DEBUGGER__.warn <<~EOS
411
420
  With Chrome browser, type the following URL in the address-bar:
412
421
 
413
- devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{@local_addr.inspect_sockaddr}/#{@uuid}
422
+ devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&noJavaScriptCompletion=true&ws=#{@local_addr.inspect_sockaddr}/#{@uuid}
414
423
 
415
424
  EOS
416
425
  end
@@ -450,7 +459,9 @@ module DEBUGGER__
450
459
  end
451
460
  end
452
461
  rescue Errno::EADDRINUSE
453
- if retry_cnt < 10
462
+ number_of_retries = @port_range.zero? ? 10 : @port_range
463
+ if retry_cnt < number_of_retries
464
+ @port += 1 unless @port_range.zero?
454
465
  retry_cnt += 1
455
466
  sleep 0.1
456
467
  retry