readapt 0.7.1 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +16 -14
  3. data/.rspec +2 -2
  4. data/.travis.yml +18 -13
  5. data/CHANGELOG.md +76 -53
  6. data/Gemfile +4 -4
  7. data/LICENSE.txt +21 -21
  8. data/README.md +37 -29
  9. data/Rakefile +14 -25
  10. data/bin/console +14 -14
  11. data/bin/setup +8 -8
  12. data/exe/readapt +5 -5
  13. data/ext/readapt/breakpoints.c +83 -88
  14. data/ext/readapt/breakpoints.h +11 -12
  15. data/ext/readapt/extconf.rb +0 -0
  16. data/ext/readapt/frame.c +137 -0
  17. data/ext/readapt/frame.h +17 -0
  18. data/ext/readapt/hash_table.c +211 -212
  19. data/ext/readapt/hash_table.h +30 -32
  20. data/ext/readapt/inspector.c +51 -0
  21. data/ext/readapt/inspector.h +8 -0
  22. data/ext/readapt/monitor.c +40 -27
  23. data/ext/readapt/monitor.h +0 -0
  24. data/ext/readapt/normalize.c +59 -53
  25. data/ext/readapt/normalize.h +7 -7
  26. data/ext/readapt/readapt.c +18 -16
  27. data/ext/readapt/stack.c +86 -0
  28. data/ext/readapt/stack.h +20 -0
  29. data/ext/readapt/threads.c +111 -17
  30. data/ext/readapt/threads.h +11 -4
  31. data/lib/readapt.rb +21 -19
  32. data/lib/readapt/adapter.rb +98 -138
  33. data/lib/readapt/breakpoint.rb +20 -13
  34. data/lib/readapt/data_reader.rb +62 -0
  35. data/lib/readapt/debugger.rb +220 -204
  36. data/lib/readapt/error.rb +63 -0
  37. data/lib/readapt/finder.rb +20 -20
  38. data/lib/readapt/frame.rb +40 -42
  39. data/lib/readapt/input.rb +7 -0
  40. data/lib/readapt/message.rb +62 -59
  41. data/lib/readapt/message/attach.rb +11 -11
  42. data/lib/readapt/message/base.rb +32 -32
  43. data/lib/readapt/message/configuration_done.rb +11 -11
  44. data/lib/readapt/message/continue.rb +15 -15
  45. data/lib/readapt/message/disconnect.rb +13 -14
  46. data/lib/readapt/message/evaluate.rb +18 -18
  47. data/lib/readapt/message/initialize.rb +13 -13
  48. data/lib/readapt/message/launch.rb +11 -11
  49. data/lib/readapt/message/next.rb +12 -12
  50. data/lib/readapt/message/pause.rb +11 -11
  51. data/lib/readapt/message/scopes.rb +26 -25
  52. data/lib/readapt/message/set_breakpoints.rb +25 -25
  53. data/lib/readapt/message/set_exception_breakpoints.rb +8 -8
  54. data/lib/readapt/message/stack_trace.rb +38 -26
  55. data/lib/readapt/message/step_in.rb +11 -11
  56. data/lib/readapt/message/step_out.rb +11 -11
  57. data/lib/readapt/message/threads.rb +18 -18
  58. data/lib/readapt/message/variables.rb +61 -57
  59. data/lib/readapt/monitor.rb +0 -0
  60. data/lib/readapt/output.rb +25 -0
  61. data/lib/readapt/references.rb +27 -0
  62. data/lib/readapt/server.rb +22 -0
  63. data/lib/readapt/shell.rb +104 -39
  64. data/lib/readapt/snapshot.rb +1 -13
  65. data/lib/readapt/thread.rb +23 -39
  66. data/lib/readapt/variable.rb +1 -1
  67. data/lib/readapt/version.rb +3 -3
  68. data/readapt.gemspec +39 -39
  69. metadata +19 -8
  70. data/lib/readapt/location.rb +0 -25
@@ -1,13 +1,20 @@
1
- module Readapt
2
- class Breakpoint
3
- attr_reader :source
4
- attr_reader :line
5
- attr_reader :condition
6
-
7
- def initialize source, line, condition
8
- @source = source
9
- @line = line
10
- @condition = condition
11
- end
12
- end
13
- end
1
+ module Readapt
2
+ class Breakpoint
3
+ attr_reader :source
4
+ attr_reader :line
5
+ attr_reader :condition
6
+ attr_reader :hit_condition
7
+ attr_writer :hit_cursor
8
+
9
+ def initialize source, line, condition, hit_condition
10
+ @source = source
11
+ @line = line
12
+ @condition = condition
13
+ @hit_condition = hit_condition
14
+ end
15
+
16
+ def hit_cursor
17
+ @hit_cursor ||= 0
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,62 @@
1
+ module Readapt
2
+ class DataReader
3
+ def initialize
4
+ @in_header = true
5
+ @content_length = 0
6
+ @buffer = String.new
7
+ end
8
+
9
+ # Declare a block to be executed for each message received from the
10
+ # client.
11
+ #
12
+ # @yieldparam [Hash] The message received from the client
13
+ def set_message_handler &block
14
+ @message_handler = block
15
+ end
16
+
17
+ # Process raw data received from the client. The data will be parsed
18
+ # into messages based on the JSON-RPC protocol. Each message will be
19
+ # passed to the block declared via set_message_handler. Incomplete data
20
+ # will be buffered and subsequent data will be appended to the buffer.
21
+ #
22
+ # @param data [String]
23
+ def receive data
24
+ data.each_char do |char|
25
+ @buffer.concat char
26
+ if @in_header
27
+ prepare_to_parse_message if @buffer.end_with?("\r\n\r\n")
28
+ else
29
+ parse_message_from_buffer if @buffer.bytesize == @content_length
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def prepare_to_parse_message
37
+ @in_header = false
38
+ @buffer.each_line do |line|
39
+ parts = line.split(':').map(&:strip)
40
+ if parts[0] == 'Content-Length'
41
+ @content_length = parts[1].to_i
42
+ break
43
+ end
44
+ end
45
+ @buffer.clear
46
+ end
47
+
48
+ def parse_message_from_buffer
49
+ begin
50
+ msg = JSON.parse(@buffer)
51
+ @message_handler.call msg unless @message_handler.nil?
52
+ rescue JSON::ParserError => e
53
+ Solargraph::Logging.logger.warn "Failed to parse request: #{e.message}"
54
+ Solargraph::Logging.logger.debug "Buffer: #{@buffer}"
55
+ ensure
56
+ @buffer.clear
57
+ @in_header = true
58
+ @content_length = 0
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,204 +1,220 @@
1
- # frozen_string_literal: true
2
-
3
- require 'backport'
4
- require 'observer'
5
- require 'set'
6
-
7
- module Readapt
8
- class Debugger
9
- include Observable
10
- include Finder
11
-
12
- attr_reader :monitor
13
-
14
- attr_reader :file
15
-
16
- def initialize machine = Machine.new
17
- @stack = []
18
- @threads = {}
19
- @frames = {}
20
- @running = false
21
- @attached = false
22
- @request = nil
23
- @config = {}
24
- @original_argv = ARGV.clone
25
- @original_prog = $0
26
- @machine = machine
27
- @breakpoints = {}
28
- end
29
-
30
- def config arguments, request
31
- @file = Readapt.normalize_path(find(arguments['program']))
32
- @config = arguments
33
- @request = request
34
- rescue LoadError => e
35
- STDERR.puts e.message
36
- end
37
-
38
- # @return [Readapt::Thread]
39
- def thread id
40
- @threads[id] || Thread::NULL_THREAD
41
- end
42
-
43
- def threads
44
- @threads.values
45
- end
46
-
47
- def frame id
48
- @frames[id] || Frame::NULL_FRAME
49
- end
50
-
51
- def launched?
52
- @request == :launch
53
- end
54
-
55
- def attached?
56
- @request == :attach
57
- end
58
-
59
- def start
60
- ::Thread.new do
61
- run { load @file }
62
- end
63
- end
64
-
65
- def run
66
- # raise RuntimeError, 'Debugger is already running' if @running
67
- set_program_args
68
- @running = true
69
- send_event('process', {
70
- name: @file
71
- })
72
- Monitor.start @file do |snapshot|
73
- debug snapshot
74
- end
75
- yield if block_given?
76
- rescue StandardError => e
77
- STDERR.puts e.message
78
- STDERR.puts e.backtrace.join("\n")
79
- rescue SystemExit
80
- # Ignore
81
- ensure
82
- Monitor.stop
83
- @running = false
84
- set_original_args
85
- changed
86
- send_event 'terminated', nil
87
- end
88
-
89
- def output data, category = :console
90
- send_event('output', {
91
- output: data,
92
- category: category
93
- })
94
- end
95
-
96
- def get_breakpoint source, line
97
- @breakpoints["#{source}:#{line}"] || Breakpoint.new(source, line, nil)
98
- end
99
-
100
- def set_breakpoint source, line, condition
101
- @breakpoints["#{source}:#{line}"] = Breakpoint.new(source, line, condition)
102
- end
103
-
104
- def clear_breakpoints source
105
- @breakpoints.delete_if { |key, value|
106
- value.source == source
107
- }
108
- end
109
-
110
- def disconnect
111
- shutdown if launched?
112
- @request = nil
113
- end
114
-
115
- def self.run &block
116
- new.run &block
117
- end
118
-
119
- private
120
-
121
- # @param [Snapshot]
122
- # return [void]
123
- def debug snapshot
124
- if snapshot.event == :thread_begin || snapshot.event == :entry
125
- @threads[snapshot.thread_id] ||= Thread.new(snapshot.thread_id)
126
- thr = @threads[snapshot.thread_id]
127
- thr.control = :continue
128
- send_event('thread', {
129
- reason: 'started',
130
- threadId: snapshot.thread_id
131
- }, true)
132
- snapshot.control = :continue
133
- elsif snapshot.event == :thread_end
134
- thr = thread(snapshot.thread_id)
135
- thr.control = :continue
136
- @threads.delete snapshot.thread_id
137
- send_event('thread', {
138
- reason: 'exited',
139
- threadId: snapshot.thread_id
140
- })
141
- snapshot.control = :continue
142
- # elsif snapshot.event == :entry
143
- # snapshot.control = :continue
144
- else
145
- if snapshot.event == :breakpoint
146
- bp = get_breakpoint(snapshot.file, snapshot.line)
147
- unless bp.condition.nil? || bp.condition.empty?
148
- # @type [Binding]
149
- bnd = ObjectSpace._id2ref(snapshot.binding_id)
150
- begin
151
- unless bnd.eval(bp.condition)
152
- snapshot.control = :continue
153
- return
154
- end
155
- rescue Exception => e
156
- STDERR.puts "Breakpoint condition raised an error"
157
- STDERR.puts "#{snapshot.file}:#{snapshot.line} - `#{bp.condition}`"
158
- STDERR.puts "[#{e.class}] #{e.message}"
159
- snapshot.control = :continue
160
- return
161
- end
162
- end
163
- end
164
- changed
165
- thread = self.thread(snapshot.thread_id)
166
- thread.control = :pause
167
- frame = Frame.new(Location.new(snapshot.file, snapshot.line), snapshot.binding_id)
168
- thread.frames.push frame
169
- @frames[frame.local_id] = frame
170
- send_event('stopped', {
171
- reason: snapshot.event,
172
- threadId: ::Thread.current.object_id
173
- })
174
- sleep 0.01 until thread.control != :pause || !@threads.key?(thread.id)
175
- @frames.delete frame.local_id
176
- thread.frames.delete frame
177
- snapshot.control = thread.control
178
- end
179
- end
180
-
181
- def set_program_args
182
- $0 = file
183
- ARGV.clear
184
- ARGV.replace(@config['programArgs'] || [])
185
- end
186
-
187
- def set_original_args
188
- $0 = @original_prog
189
- ARGV.clear
190
- ARGV.replace @original_argv
191
- end
192
-
193
- def shutdown
194
- @machine.stop
195
- exit
196
- end
197
-
198
- def send_event event, data, wait = false
199
- changed
200
- notify_observers event, data
201
- sleep 0.01 if wait
202
- end
203
- end
204
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'backport'
4
+ require 'observer'
5
+ require 'set'
6
+
7
+ module Readapt
8
+ class Debugger
9
+ include Observable
10
+ include Finder
11
+
12
+ attr_reader :monitor
13
+
14
+ attr_reader :file
15
+
16
+ def initialize
17
+ @stack = []
18
+ @frames = {}
19
+ @running = false
20
+ @attached = false
21
+ @request = nil
22
+ @config = {}
23
+ @original_argv = ARGV.clone
24
+ @original_prog = $0
25
+ @breakpoints = {}
26
+ end
27
+
28
+ def config arguments, request
29
+ @file = Readapt.normalize_path(find(arguments['program']))
30
+ @config = arguments
31
+ @request = request
32
+ rescue LoadError => e
33
+ STDERR.puts e.message
34
+ end
35
+
36
+ # @return [Readapt::Thread]
37
+ def thread id
38
+ Thread.find(id)
39
+ end
40
+
41
+ def threads
42
+ Thread.all
43
+ end
44
+
45
+ def frame id
46
+ @frames[id] || Frame::NULL_FRAME
47
+ end
48
+
49
+ def launched?
50
+ @request == :launch
51
+ end
52
+
53
+ def attached?
54
+ @request == :attach
55
+ end
56
+
57
+ def start
58
+ ::Thread.new do
59
+ set_program_args
60
+ run { load @file }
61
+ set_original_args
62
+ end
63
+ end
64
+
65
+ def run
66
+ # raise RuntimeError, 'Debugger is already running' if @running
67
+ @running = true
68
+ send_event('process', {
69
+ name: @file
70
+ })
71
+ Monitor.start @file do |snapshot|
72
+ debug snapshot
73
+ end
74
+ yield if block_given?
75
+ rescue StandardError => e
76
+ STDERR.puts "[#{e.class}] #{e.message}"
77
+ STDERR.puts e.backtrace.join("\n")
78
+ ensure
79
+ Monitor.stop
80
+ @running = false
81
+ STDOUT.flush #unless STDOUT.closed?
82
+ STDERR.flush #unless STDERR.closed?
83
+ changed
84
+ send_event 'terminated', nil
85
+ end
86
+
87
+ def output data, category = :console
88
+ send_event('output', {
89
+ output: data,
90
+ category: category
91
+ })
92
+ end
93
+
94
+ def get_breakpoint source, line
95
+ @breakpoints["#{source}:#{line}"] || Breakpoint.new(source, line, nil, 0)
96
+ end
97
+
98
+ def set_breakpoint source, line, condition, hitcount
99
+ @breakpoints["#{source}:#{line}"] = Breakpoint.new(source, line, condition, hitcount)
100
+ end
101
+
102
+ def clear_breakpoints source
103
+ @breakpoints.delete_if { |_key, value|
104
+ value.source == source
105
+ }
106
+ end
107
+
108
+ def disconnect
109
+ shutdown if launched?
110
+ @request = nil
111
+ end
112
+
113
+ def self.run &block
114
+ new.run &block
115
+ end
116
+
117
+ private
118
+
119
+ # @param [Snapshot]
120
+ # return [void]
121
+ def debug snapshot
122
+ sleep 0.001 # @todo Trying to let thread data sync
123
+ References.clear
124
+ if snapshot.event == :thread_begin || snapshot.event == :entry
125
+ thr = Thread.find(snapshot.thread_id)
126
+ thr.control = :continue
127
+ send_event('thread', {
128
+ reason: 'started',
129
+ threadId: snapshot.thread_id
130
+ }, true)
131
+ snapshot.control = :continue
132
+ elsif snapshot.event == :thread_end
133
+ thr = thread(snapshot.thread_id)
134
+ thr.control = :continue
135
+ send_event('thread', {
136
+ reason: 'exited',
137
+ threadId: snapshot.thread_id
138
+ })
139
+ snapshot.control = :continue
140
+ else
141
+ confirmed_pause = true
142
+ thread = self.thread(snapshot.thread_id)
143
+ if snapshot.event == :breakpoint
144
+ bp = get_breakpoint(snapshot.file, snapshot.line)
145
+ unless bp.condition.nil? || bp.condition.empty?
146
+ # @type [Binding]
147
+ bnd = thread.frames.first.frame_binding
148
+ begin
149
+ unless bnd.eval(bp.condition)
150
+ confirmed_pause = false
151
+ end
152
+ rescue Exception => e
153
+ STDERR.puts "Breakpoint condition raised an error"
154
+ STDERR.puts "#{snapshot.file}:#{snapshot.line} - `#{bp.condition}`"
155
+ STDERR.puts "[#{e.class}] #{e.message}"
156
+ confirmed_pause = false
157
+ end
158
+ end
159
+ unless !confirmed_pause || bp.hit_condition.nil? || bp.hit_condition.empty?
160
+ bp.hit_cursor += 1
161
+ bnd = thread.frames.first.frame_binding
162
+ begin
163
+ hit_count = bnd.eval(bp.hit_condition)
164
+ if bp.hit_cursor == hit_count
165
+ bp.hit_cursor = 0
166
+ else
167
+ confirmed_pause = false
168
+ end
169
+ rescue Exception => e
170
+ STDERR.puts "Breakpoint condition raised an error"
171
+ STDERR.puts "#{snapshot.file}:#{snapshot.line} - `#{bp.condition}`"
172
+ STDERR.puts "[#{e.class}] #{e.message}"
173
+ confirmed_pause = false
174
+ end
175
+ end
176
+ end
177
+ if confirmed_pause
178
+ changed
179
+ thread.control = :pause
180
+ thread.frames.each do |frm|
181
+ @frames[frm.local_id] = frm
182
+ end
183
+ send_event('stopped', {
184
+ reason: snapshot.event,
185
+ threadId: thread.id
186
+ })
187
+ sleep 0.01 until thread.control != :pause || !Thread.include?(thread.id)
188
+ thread.frames.each do |frm|
189
+ @frames.delete frm.local_id
190
+ end
191
+ else
192
+ thread.control = :continue
193
+ end
194
+ snapshot.control = thread.control
195
+ end
196
+ end
197
+
198
+ def set_program_args
199
+ $0 = file
200
+ ARGV.clear
201
+ ARGV.replace(@config['programArgs'] || [])
202
+ end
203
+
204
+ def set_original_args
205
+ $0 = @original_prog
206
+ ARGV.clear
207
+ ARGV.replace @original_argv
208
+ end
209
+
210
+ def shutdown
211
+ exit
212
+ end
213
+
214
+ def send_event event, data, wait = false
215
+ changed
216
+ notify_observers event, data
217
+ sleep 0.01 if wait
218
+ end
219
+ end
220
+ end