readapt 0.7.1 → 1.1.1
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/.gitignore +16 -14
- data/.rspec +2 -2
- data/.travis.yml +18 -13
- data/CHANGELOG.md +76 -53
- data/Gemfile +4 -4
- data/LICENSE.txt +21 -21
- data/README.md +37 -29
- data/Rakefile +14 -25
- data/bin/console +14 -14
- data/bin/setup +8 -8
- data/exe/readapt +5 -5
- data/ext/readapt/breakpoints.c +83 -88
- data/ext/readapt/breakpoints.h +11 -12
- data/ext/readapt/extconf.rb +0 -0
- data/ext/readapt/frame.c +137 -0
- data/ext/readapt/frame.h +17 -0
- data/ext/readapt/hash_table.c +211 -212
- data/ext/readapt/hash_table.h +30 -32
- data/ext/readapt/inspector.c +51 -0
- data/ext/readapt/inspector.h +8 -0
- data/ext/readapt/monitor.c +40 -27
- data/ext/readapt/monitor.h +0 -0
- data/ext/readapt/normalize.c +59 -53
- data/ext/readapt/normalize.h +7 -7
- data/ext/readapt/readapt.c +18 -16
- data/ext/readapt/stack.c +86 -0
- data/ext/readapt/stack.h +20 -0
- data/ext/readapt/threads.c +111 -17
- data/ext/readapt/threads.h +11 -4
- data/lib/readapt.rb +21 -19
- data/lib/readapt/adapter.rb +98 -138
- data/lib/readapt/breakpoint.rb +20 -13
- data/lib/readapt/data_reader.rb +62 -0
- data/lib/readapt/debugger.rb +220 -204
- data/lib/readapt/error.rb +63 -0
- data/lib/readapt/finder.rb +20 -20
- data/lib/readapt/frame.rb +40 -42
- data/lib/readapt/input.rb +7 -0
- data/lib/readapt/message.rb +62 -59
- data/lib/readapt/message/attach.rb +11 -11
- data/lib/readapt/message/base.rb +32 -32
- data/lib/readapt/message/configuration_done.rb +11 -11
- data/lib/readapt/message/continue.rb +15 -15
- data/lib/readapt/message/disconnect.rb +13 -14
- data/lib/readapt/message/evaluate.rb +18 -18
- data/lib/readapt/message/initialize.rb +13 -13
- data/lib/readapt/message/launch.rb +11 -11
- data/lib/readapt/message/next.rb +12 -12
- data/lib/readapt/message/pause.rb +11 -11
- data/lib/readapt/message/scopes.rb +26 -25
- data/lib/readapt/message/set_breakpoints.rb +25 -25
- data/lib/readapt/message/set_exception_breakpoints.rb +8 -8
- data/lib/readapt/message/stack_trace.rb +38 -26
- data/lib/readapt/message/step_in.rb +11 -11
- data/lib/readapt/message/step_out.rb +11 -11
- data/lib/readapt/message/threads.rb +18 -18
- data/lib/readapt/message/variables.rb +61 -57
- data/lib/readapt/monitor.rb +0 -0
- data/lib/readapt/output.rb +25 -0
- data/lib/readapt/references.rb +27 -0
- data/lib/readapt/server.rb +22 -0
- data/lib/readapt/shell.rb +104 -39
- data/lib/readapt/snapshot.rb +1 -13
- data/lib/readapt/thread.rb +23 -39
- data/lib/readapt/variable.rb +1 -1
- data/lib/readapt/version.rb +3 -3
- data/readapt.gemspec +39 -39
- metadata +19 -8
- data/lib/readapt/location.rb +0 -25
data/lib/readapt/breakpoint.rb
CHANGED
@@ -1,13 +1,20 @@
|
|
1
|
-
module Readapt
|
2
|
-
class Breakpoint
|
3
|
-
attr_reader :source
|
4
|
-
attr_reader :line
|
5
|
-
attr_reader :condition
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
data/lib/readapt/debugger.rb
CHANGED
@@ -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
|
17
|
-
@stack = []
|
18
|
-
@
|
19
|
-
@
|
20
|
-
@
|
21
|
-
@
|
22
|
-
@
|
23
|
-
@
|
24
|
-
@
|
25
|
-
@
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def run
|
66
|
-
# raise RuntimeError, 'Debugger is already running' if @running
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
STDERR.puts e.
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
if snapshot.event == :thread_begin || snapshot.event == :entry
|
125
|
-
|
126
|
-
thr =
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
thr =
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
end
|
197
|
-
|
198
|
-
def
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
end
|
203
|
-
|
204
|
-
|
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
|