debug 1.4.0 → 1.5.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.
@@ -4,72 +4,112 @@ require_relative 'color'
4
4
 
5
5
  module DEBUGGER__
6
6
  class SourceRepository
7
- SrcInfo = Struct.new(:src, :colored)
7
+ include Color
8
8
 
9
- def initialize
10
- @files = {} # filename => SrcInfo
11
- end
9
+ if RubyVM.respond_to? :keep_script_lines
10
+ # Ruby 3.1 and later
11
+ RubyVM.keep_script_lines = true
12
+ require 'objspace'
12
13
 
13
- def add iseq, src
14
- if (path = iseq.absolute_path) && File.exist?(path)
15
- add_path path
16
- elsif src
17
- add_iseq iseq, src
14
+ def initialize
15
+ # cache
16
+ @cmap = ObjectSpace::WeakMap.new
18
17
  end
19
- end
20
18
 
21
- def all_iseq iseq, rs = []
22
- rs << iseq
23
- iseq.each_child{|ci|
24
- all_iseq(ci, rs)
25
- }
26
- rs
27
- end
19
+ def add iseq, src
20
+ # do nothing
21
+ end
22
+
23
+ def get iseq
24
+ return unless iseq
28
25
 
29
- private def add_iseq iseq, src
30
- line = iseq.first_line
31
- if line > 1
32
- src = ("\n" * (line - 1)) + src
26
+ if lines = iseq.script_lines&.map(&:chomp)
27
+ lines
28
+ else
29
+ if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
30
+ File.readlines(path, chomp: true)
31
+ else
32
+ nil
33
+ end
34
+ end
33
35
  end
34
- si = SrcInfo.new(src.lines)
35
36
 
36
- all_iseq(iseq).each{|e|
37
- e.instance_variable_set(:@debugger_si, si)
38
- e.freeze
39
- }
40
- end
37
+ def get_colored iseq
38
+ if lines = @cmap[iseq]
39
+ lines
40
+ else
41
+ if src_lines = get(iseq)
42
+ @cmap[iseq] = colorize_code(src_lines.join("\n")).lines
43
+ else
44
+ nil
45
+ end
46
+ end
47
+ end
48
+ else
49
+ # ruby 3.0 or earlier
50
+ SrcInfo = Struct.new(:src, :colored)
41
51
 
42
- private def add_path path
43
- src = File.read(path)
44
- src = src.gsub("\r\n", "\n") # CRLF -> LF
45
- @files[path] = SrcInfo.new(src.lines)
46
- rescue SystemCallError
47
- end
52
+ def initialize
53
+ @files = {} # filename => SrcInfo
54
+ end
48
55
 
49
- private def get_si iseq
50
- return unless iseq
56
+ def add iseq, src
57
+ if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
58
+ add_path path
59
+ elsif src
60
+ add_iseq iseq, src
61
+ end
62
+ end
51
63
 
52
- if iseq.instance_variable_defined?(:@debugger_si)
53
- iseq.instance_variable_get(:@debugger_si)
54
- elsif @files.has_key?(path = iseq.absolute_path)
55
- @files[path]
56
- elsif path
57
- add_path(path)
64
+ private def all_iseq iseq, rs = []
65
+ rs << iseq
66
+ iseq.each_child{|ci|
67
+ all_iseq(ci, rs)
68
+ }
69
+ rs
58
70
  end
59
- end
60
71
 
61
- def get iseq
62
- if si = get_si(iseq)
63
- si.src
72
+ private def add_iseq iseq, src
73
+ line = iseq.first_line
74
+ if line > 1
75
+ src = ("\n" * (line - 1)) + src
76
+ end
77
+ si = SrcInfo.new(src.lines)
78
+ all_iseq(iseq).each{|e|
79
+ e.instance_variable_set(:@debugger_si, si)
80
+ e.freeze
81
+ }
64
82
  end
65
- end
66
83
 
67
- include Color
84
+ private def add_path path
85
+ src_lines = File.readlines(path, chomp: true)
86
+ @files[path] = SrcInfo.new(src_lines)
87
+ rescue SystemCallError
88
+ end
89
+
90
+ private def get_si iseq
91
+ return unless iseq
92
+
93
+ if iseq.instance_variable_defined?(:@debugger_si)
94
+ iseq.instance_variable_get(:@debugger_si)
95
+ elsif @files.has_key?(path = (iseq.absolute_path || iseq.path))
96
+ @files[path]
97
+ elsif path
98
+ add_path(path)
99
+ end
100
+ end
101
+
102
+ def get iseq
103
+ if si = get_si(iseq)
104
+ si.src
105
+ end
106
+ end
68
107
 
69
- def get_colored iseq
70
- if si = get_si(iseq)
71
- si.colored || begin
72
- si.colored = colorize_code(si.src.join).lines
108
+ def get_colored iseq
109
+ if si = get_si(iseq)
110
+ si.colored || begin
111
+ si.colored = colorize_code(si.src.join("\n")).lines
112
+ end
73
113
  end
74
114
  end
75
115
  end
@@ -8,7 +8,11 @@ require_relative 'color'
8
8
  module DEBUGGER__
9
9
  module SkipPathHelper
10
10
  def skip_path?(path)
11
- (skip_paths = CONFIG[:skip_path]) && skip_paths.any?{|skip_path| path.match?(skip_path)}
11
+ !path || skip_internal_path?(path) || (skip_paths = CONFIG[:skip_path]) && skip_paths.any?{|skip_path| path.match?(skip_path)}
12
+ end
13
+
14
+ def skip_internal_path?(path)
15
+ path.start_with?(__dir__) || path.start_with?('<internal:')
12
16
  end
13
17
 
14
18
  def skip_location?(loc)
@@ -30,7 +34,11 @@ module DEBUGGER__
30
34
  include Color
31
35
  include SkipPathHelper
32
36
 
33
- attr_reader :location, :thread, :id, :recorder
37
+ attr_reader :thread, :id, :recorder
38
+
39
+ def location
40
+ current_frame&.location
41
+ end
34
42
 
35
43
  def assemble_arguments(args)
36
44
  args.map do |arg|
@@ -149,14 +157,7 @@ module DEBUGGER__
149
157
  end
150
158
 
151
159
  def to_s
152
- loc = current_frame&.location
153
-
154
- if loc
155
- str = "(#{@thread.name || @thread.status})@#{loc}"
156
- else
157
- str = "(#{@thread.name || @thread.status})@#{@thread.to_s}"
158
- end
159
-
160
+ str = "(#{@thread.name || @thread.status})@#{current_frame&.location || @thread.to_s}"
160
161
  str += " (not under control)" unless self.waiting?
161
162
  str
162
163
  end
@@ -246,7 +247,6 @@ module DEBUGGER__
246
247
 
247
248
  cf = @target_frames.first
248
249
  if cf
249
- @location = cf.location
250
250
  case event
251
251
  when :return, :b_return, :c_return
252
252
  cf.has_return_value = true
@@ -339,9 +339,45 @@ module DEBUGGER__
339
339
 
340
340
  ## cmd helpers
341
341
 
342
- # this method is extracted to hide frame_eval's local variables from C method eval's binding
343
- def instance_eval_for_cmethod frame_self, src
344
- frame_self.instance_eval(src)
342
+ if TracePoint.respond_to? :allow_reentry
343
+ def tp_allow_reentry
344
+ TracePoint.allow_reentry do
345
+ yield
346
+ end
347
+ rescue RuntimeError => e
348
+ # on the postmortem mode, it is not stopped in TracePoint
349
+ if e.message == 'No need to allow reentrance.'
350
+ yield
351
+ else
352
+ raise
353
+ end
354
+ end
355
+ else
356
+ def tp_allow_reentry
357
+ yield
358
+ end
359
+ end
360
+
361
+ def frame_eval_core src, b
362
+ saved_target_frames = @target_frames
363
+ saved_current_frame_index = @current_frame_index
364
+
365
+ if b
366
+ f, _l = b.source_location
367
+
368
+ tp_allow_reentry do
369
+ b.eval(src, "(rdbg)/#{f}")
370
+ end
371
+ else
372
+ frame_self = current_frame.self
373
+
374
+ tp_allow_reentry do
375
+ frame_self.instance_eval(src)
376
+ end
377
+ end
378
+ ensure
379
+ @target_frames = saved_target_frames
380
+ @current_frame_index = saved_current_frame_index
345
381
  end
346
382
 
347
383
  SPECIAL_LOCAL_VARS = [
@@ -358,16 +394,13 @@ module DEBUGGER__
358
394
  b.local_variable_set(name, var) if /\%/ !~ name
359
395
  end
360
396
 
361
- result = if b
362
- f, _l = b.source_location
363
- b.eval(src, "(rdbg)/#{f}")
364
- else
365
- frame_self = current_frame.self
366
- instance_eval_for_cmethod(frame_self, src)
367
- end
397
+ result = frame_eval_core(src, b)
398
+
368
399
  @success_last_eval = true
369
400
  result
370
401
 
402
+ rescue SystemExit
403
+ raise
371
404
  rescue Exception => e
372
405
  return yield(e) if block_given?
373
406
 
@@ -432,13 +465,36 @@ module DEBUGGER__
432
465
  end
433
466
 
434
467
  def current_frame
468
+ get_frame(@current_frame_index)
469
+ end
470
+
471
+ def get_frame(index)
435
472
  if @target_frames
436
- @target_frames[@current_frame_index]
473
+ @target_frames[index]
437
474
  else
438
475
  nil
439
476
  end
440
477
  end
441
478
 
479
+ def collect_locals(frame)
480
+ locals = []
481
+
482
+ if s = frame&.self
483
+ locals << ["%self", s]
484
+ end
485
+ special_local_variables frame do |name, val|
486
+ locals << [name, val]
487
+ end
488
+
489
+ if vars = frame&.local_variables
490
+ vars.each{|var, val|
491
+ locals << [var, val]
492
+ }
493
+ end
494
+
495
+ locals
496
+ end
497
+
442
498
  ## cmd: show
443
499
 
444
500
  def special_local_variables frame
@@ -450,17 +506,8 @@ module DEBUGGER__
450
506
  end
451
507
 
452
508
  def show_locals pat
453
- if s = current_frame&.self
454
- puts_variable_info '%self', s, pat
455
- end
456
- special_local_variables current_frame do |name, val|
457
- puts_variable_info name, val, pat
458
- end
459
-
460
- if vars = current_frame&.local_variables
461
- vars.each{|var, val|
462
- puts_variable_info var, val, pat
463
- }
509
+ collect_locals(current_frame).each do |var, val|
510
+ puts_variable_info(var, val, pat)
464
511
  end
465
512
  end
466
513
 
@@ -526,28 +573,32 @@ module DEBUGGER__
526
573
  w = SESSION::width
527
574
 
528
575
  if mono_info.length >= w
529
- info = truncate(mono_info, width: w)
576
+ maximum_value_width = w - "#{label} = ".length
577
+ valstr = truncate(inspected, width: maximum_value_width)
530
578
  else
531
579
  valstr = colored_inspect(obj, width: 2 ** 30)
532
580
  valstr = inspected if valstr.lines.size > 1
533
- info = "#{colorize_cyan(label)} = #{valstr}"
534
581
  end
535
582
 
583
+ info = "#{colorize_cyan(label)} = #{valstr}"
584
+
536
585
  puts info
537
586
  end
538
587
 
539
588
  def truncate(string, width:)
540
- str = string[0 .. (width-4)] + '...'
541
- str += ">" if str.start_with?("#<")
542
- str
589
+ if string.start_with?("#<")
590
+ string[0 .. (width-5)] + '...>'
591
+ else
592
+ string[0 .. (width-4)] + '...'
593
+ end
543
594
  end
544
595
 
545
596
  ### cmd: show edit
546
597
 
547
598
  def show_by_editor path = nil
548
599
  unless path
549
- if @target_frames && frame = @target_frames[@current_frame_index]
550
- path = frame.path
600
+ if current_frame
601
+ path = current_frame.path
551
602
  else
552
603
  return # can't get path
553
604
  end
@@ -853,6 +904,7 @@ module DEBUGGER__
853
904
  else
854
905
  raise "unsupported frame operation: #{arg.inspect}"
855
906
  end
907
+
856
908
  event! :result, nil
857
909
 
858
910
  when :show
@@ -1001,8 +1053,8 @@ module DEBUGGER__
1001
1053
 
1002
1054
  @tp_recorder ||= TracePoint.new(:line){|tp|
1003
1055
  next unless Thread.current == thread
1004
- next if tp.path.start_with? __dir__
1005
- next if tp.path.start_with? '<internal:'
1056
+ # can't be replaced by skip_location
1057
+ next if skip_internal_path?(tp.path)
1006
1058
  loc = caller_locations(1, 1).first
1007
1059
  next if skip_location?(loc)
1008
1060
 
data/lib/debug/tracer.rb CHANGED
@@ -66,15 +66,7 @@ module DEBUGGER__
66
66
  end
67
67
 
68
68
  def skip? tp
69
- if tp.path.start_with?(__dir__) ||
70
- tp.path.start_with?('<internal:') ||
71
- ThreadClient.current.management? ||
72
- skip_path?(tp.path) ||
73
- skip_with_pattern?(tp)
74
- true
75
- else
76
- false
77
- end
69
+ ThreadClient.current.management? || skip_path?(tp.path) || skip_with_pattern?(tp)
78
70
  end
79
71
 
80
72
  def skip_with_pattern?(tp)
@@ -89,11 +81,13 @@ module DEBUGGER__
89
81
  ThreadClient.current.on_trace self.object_id, buff
90
82
  else
91
83
  @output.puts buff
84
+ @output.flush
92
85
  end
93
86
  end
94
87
 
95
88
  def puts msg
96
89
  @output.puts msg
90
+ @output.flush
97
91
  end
98
92
 
99
93
  def minfo tp
data/lib/debug/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DEBUGGER__
4
- VERSION = "1.4.0"
4
+ VERSION = "1.5.0"
5
5
  end
data/misc/README.md.erb CHANGED
@@ -1,8 +1,8 @@
1
- [![Ruby](https://github.com/ruby/debug/actions/workflows/ruby.yml/badge.svg?branch=master)](https://github.com/ruby/debug/actions/workflows/ruby.yml?query=branch%3Amaster)
1
+ [![Ruby](https://github.com/ruby/debug/actions/workflows/ruby.yml/badge.svg?branch=master)](https://github.com/ruby/debug/actions/workflows/ruby.yml?query=branch%3Amaster) [![Protocol](https://github.com/ruby/debug/actions/workflows/protocol.yml/badge.svg)](https://github.com/ruby/debug/actions/workflows/protocol.yml)
2
2
 
3
3
  # debug.rb
4
4
 
5
- This library provides debugging functionality to Ruby.
5
+ This library provides debugging functionality to Ruby (MRI) 2.6 and later.
6
6
 
7
7
  This debug.rb is replacement of traditional lib/debug.rb standard library which is implemented by `set_trace_func`.
8
8
  New debug.rb has several advantages:
@@ -113,7 +113,7 @@ d => nil
113
113
  5| binding.break
114
114
  6| c = 3
115
115
  7| d = 4
116
- => 8| binding.break # Again the program stops at here
116
+ => 8| binding.break # Again the program stops here
117
117
  9| p [a, b, c, d]
118
118
  10|
119
119
  11| __END__
@@ -358,6 +358,8 @@ Also `open` command allows opening the debug port.
358
358
 
359
359
  #### VSCode integration
360
360
 
361
+ ([vscode-rdbg v0.0.9](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg) or later is required)
362
+
361
363
  If you don't run a debuggee Ruby process on VSCode, you can attach with VSCode later with the following steps.
362
364
 
363
365
  `rdbg --open=vscode` opens the debug port and tries to invoke the VSCode (`code` command).
@@ -417,7 +419,7 @@ Note that you can attach with `rdbg --attach` and continue REPL debugging.
417
419
 
418
420
  #### Chrome DevTool integration
419
421
 
420
- With `rdbg --open=chrome` command will shows the following message.
422
+ With `rdbg --open=chrome` command will show the following message.
421
423
 
422
424
  ```
423
425
  $ rdbg target.rb --open=chrome
@@ -435,8 +437,6 @@ Also `open chrome` command works like `open vscode`.
435
437
 
436
438
  For more information about how to use Chrome debugging, you might want to read [here](https://developer.chrome.com/docs/devtools/).
437
439
 
438
- Note: If you want to maximize Chrome DevTools, click [Toggle Device Toolbar](https://developer.chrome.com/docs/devtools/device-mode/#viewport).
439
-
440
440
  ## Configuration
441
441
 
442
442
  You can configure the debugger's behavior with debug commands and environment variables.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: debug
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koichi Sasada
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-17 00:00:00.000000000 Z
11
+ date: 2022-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: irb
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.2.7
41
41
  description: Debugging functionality for Ruby. This is completely rewritten debug.rb
42
- which was contained by the encient Ruby versions.
42
+ which was contained by the ancient Ruby versions.
43
43
  email:
44
44
  - ko1@atdot.net
45
45
  executables:
@@ -48,21 +48,12 @@ extensions:
48
48
  - ext/debug/extconf.rb
49
49
  extra_rdoc_files: []
50
50
  files:
51
- - ".github/ISSUE_TEMPLATE/bug_report.md"
52
- - ".github/ISSUE_TEMPLATE/custom.md"
53
- - ".github/ISSUE_TEMPLATE/feature_request.md"
54
- - ".github/pull_request_template.md"
55
- - ".github/workflows/ruby.yml"
56
- - ".gitignore"
57
51
  - CONTRIBUTING.md
58
52
  - Gemfile
59
53
  - LICENSE.txt
60
54
  - README.md
61
55
  - Rakefile
62
56
  - TODO.md
63
- - bin/console
64
- - bin/gentest
65
- - bin/setup
66
57
  - debug.gemspec
67
58
  - exe/rdbg
68
59
  - ext/debug/debug.c
@@ -112,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
103
  - !ruby/object:Gem::Version
113
104
  version: '0'
114
105
  requirements: []
115
- rubygems_version: 3.1.6
106
+ rubygems_version: 3.4.0.dev
116
107
  signing_key:
117
108
  specification_version: 4
118
109
  summary: Debugging functionality for Ruby
@@ -1,24 +0,0 @@
1
- ---
2
- name: Bug report
3
- about: Create a report to help us improve
4
- title: ''
5
- labels: ''
6
- assignees: ''
7
-
8
- ---
9
-
10
- **Your environment**
11
-
12
- * `ruby -v`:
13
- * `rdbg -v`:
14
-
15
- **Describe the bug**
16
- A clear and concise description of what the bug is.
17
-
18
- **To Reproduce**
19
-
20
- **Expected behavior**
21
- A clear and concise description of what you expected to happen.
22
-
23
- **Additional context**
24
- Add any other context about the problem here.
@@ -1,10 +0,0 @@
1
- ---
2
- name: Custom issue template
3
- about: Blank issue
4
- title: ''
5
- labels: ''
6
- assignees: ''
7
-
8
- ---
9
-
10
-
@@ -1,14 +0,0 @@
1
- ---
2
- name: Feature request
3
- about: Suggest an idea for this project
4
- title: ''
5
- labels: ''
6
- assignees: ''
7
-
8
- ---
9
-
10
- **Your proposal**
11
- What is your idea?
12
-
13
- **Additional context**
14
- Add any other context or screenshots about the feature request here.
@@ -1,9 +0,0 @@
1
- Thanks for your Pull Request 🎉
2
-
3
- **Please follow these instructions to help us review it more efficiently:**
4
-
5
- - Add references of related issues/PRs in the description if available.
6
- - If you're updating the readme file, make sure you followed [the instruction here](https://github.com/ruby/debug/blob/master/CONTRIBUTING.md#to-update-readme).
7
-
8
- ## Description
9
- Describe your changes:
@@ -1,34 +0,0 @@
1
- # This workflow uses actions that are not certified by GitHub.
2
- # They are provided by a third-party and are governed by
3
- # separate terms of service, privacy policy, and support
4
- # documentation.
5
- # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
- # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
-
8
- name: Ruby
9
-
10
- on:
11
- push:
12
- branches: [ master ]
13
- pull_request:
14
- branches: [ master ]
15
-
16
- jobs:
17
- test:
18
-
19
- runs-on: ubuntu-latest
20
- strategy:
21
- matrix:
22
- ruby-version: ['2.6', '2.7', '3.0', 'head', 'debug']
23
-
24
- steps:
25
- - uses: actions/checkout@v2
26
- - name: Set up Ruby
27
- # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
28
- # change this to (see https://github.com/ruby/setup-ruby#versioning):
29
- uses: ruby/setup-ruby@v1
30
- with:
31
- ruby-version: ${{ matrix.ruby-version }}
32
- bundler-cache: true # runs 'bundle install' and caches installed gems automatically
33
- - name: Run tests
34
- run: bundle exec rake
data/.gitignore DELETED
@@ -1,12 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
- *.bundle
10
- /Gemfile.lock
11
- /lib/debug/debug.so
12
- .ruby-version
data/bin/console DELETED
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "debug"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start(__FILE__)