debug 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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__)