readapt 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +13 -0
  5. data/CHANGELOG.md +15 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +29 -0
  9. data/Rakefile +25 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/exe/readapt +5 -0
  13. data/ext/readapt/extconf.rb +3 -0
  14. data/ext/readapt/monitor.c +332 -0
  15. data/ext/readapt/monitor.h +1 -0
  16. data/ext/readapt/readapt.c +12 -0
  17. data/ext/readapt/threads.c +101 -0
  18. data/ext/readapt/threads.h +18 -0
  19. data/lib/readapt.rb +34 -0
  20. data/lib/readapt/adapter.rb +138 -0
  21. data/lib/readapt/breakpoint.rb +16 -0
  22. data/lib/readapt/breakpoints.rb +58 -0
  23. data/lib/readapt/debugger.rb +173 -0
  24. data/lib/readapt/finder.rb +20 -0
  25. data/lib/readapt/frame.rb +68 -0
  26. data/lib/readapt/location.rb +25 -0
  27. data/lib/readapt/message.rb +57 -0
  28. data/lib/readapt/message/attach.rb +11 -0
  29. data/lib/readapt/message/base.rb +32 -0
  30. data/lib/readapt/message/configuration_done.rb +11 -0
  31. data/lib/readapt/message/continue.rb +15 -0
  32. data/lib/readapt/message/disconnect.rb +14 -0
  33. data/lib/readapt/message/initialize.rb +13 -0
  34. data/lib/readapt/message/launch.rb +11 -0
  35. data/lib/readapt/message/next.rb +12 -0
  36. data/lib/readapt/message/pause.rb +11 -0
  37. data/lib/readapt/message/scopes.rb +25 -0
  38. data/lib/readapt/message/set_breakpoints.rb +26 -0
  39. data/lib/readapt/message/set_exception_breakpoints.rb +8 -0
  40. data/lib/readapt/message/stack_trace.rb +26 -0
  41. data/lib/readapt/message/step_in.rb +11 -0
  42. data/lib/readapt/message/step_out.rb +11 -0
  43. data/lib/readapt/message/threads.rb +18 -0
  44. data/lib/readapt/message/variables.rb +57 -0
  45. data/lib/readapt/monitor.rb +31 -0
  46. data/lib/readapt/shell.rb +48 -0
  47. data/lib/readapt/snapshot.rb +50 -0
  48. data/lib/readapt/thread.rb +39 -0
  49. data/lib/readapt/variable.rb +70 -0
  50. data/lib/readapt/version.rb +3 -0
  51. data/readapt.gemspec +39 -0
  52. metadata +184 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c67633f1de57cb29613083f2e241ad6ca9c3f86b0d273fbd1e78e86d7a804b37
4
+ data.tar.gz: 0c3994366532e303edc51afd01642a3417c8247ef5af5d9cd16942b385de41c3
5
+ SHA512:
6
+ metadata.gz: 9cd6fa0b2a4a3f17e9bc8ef2e44fc8b553667ece8eff3313c8be1dc582a55436f4a909ed33fcd4b01d73e0a2f77b6f3a99dca9c94f66260e8b57193cb01f9276
7
+ data.tar.gz: bdbda94a34fb6c36cc8ad867ca5def4ea950d03066ef07b6b2e7b44819ea77e13382594ecf909464850cb99aa2d76933e1d37a932a12e48da904c65a13180995
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.so
10
+ /.vscode/
11
+ Gemfile.lock
12
+
13
+ # rspec failure tracking
14
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ rvm:
5
+ - 2.2
6
+ - 2.3
7
+ - 2.4
8
+ - 2.5
9
+ - 2.6
10
+ before_script:
11
+ - bundle install
12
+ - rake compile
13
+ script: rspec
data/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # 0.3.1 - August 10. 2019
2
+ - Require Ruby >= 2.2
3
+
4
+ # 0.3.0 - August 9. 2019
5
+ - Synchronized events
6
+ - Handle multiple paused threads
7
+ - Isolate the Backport machine
8
+
9
+ # 0.2.0 - August 7, 2019
10
+ - Find external programs
11
+ - Improved stdout/stderr redirects
12
+ - Individual thread control
13
+
14
+ # 0.1.0 - August 5, 2019
15
+ - First release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in readapt.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Fred Snyder
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Readapt
2
+
3
+ A Ruby debugger that supports the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/specification).
4
+
5
+ *This gem is currently in early development*.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'readapt'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install readapt
22
+
23
+ ## Usage
24
+
25
+ Run `readapt serve` to start the server. The default client connection is host 127.0.0.1, port 1234.
26
+
27
+ ## Integrations
28
+
29
+ Readapt is available for Visual Studio Code in the [Ruby Debug extension](https://marketplace.visualstudio.com/items?itemName=castwide.ruby-debug).
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "rake/extensiontask"
4
+ require 'readapt/version'
5
+ require 'tmpdir'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task :default => :spec
10
+
11
+ # Compile tasks
12
+ Rake::ExtensionTask.new "readapt" do |ext|
13
+ ext.lib_dir = "lib/readapt"
14
+ end
15
+
16
+ namespace :install do
17
+ desc 'Install on Windows'
18
+ task :win do
19
+ Dir.mktmpdir do |tmp|
20
+ gemfile = File.join(tmp, 'readapt.gem')
21
+ system("gem build readapt.gemspec -o #{gemfile}") &&
22
+ system("gem install #{gemfile}")
23
+ end
24
+ end
25
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "readapt"
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__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/readapt ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "readapt"
4
+
5
+ Readapt::Shell.start(ARGV)
@@ -0,0 +1,3 @@
1
+ require "mkmf"
2
+
3
+ create_makefile "readapt/readapt"
@@ -0,0 +1,332 @@
1
+ #include "ruby.h"
2
+ #include "ruby/debug.h"
3
+ #include "threads.h"
4
+
5
+ static VALUE m_Monitor;
6
+ static VALUE c_Snapshot;
7
+
8
+ static VALUE tpLine;
9
+ static VALUE tpCall;
10
+ static VALUE tpReturn;
11
+ static VALUE tpThreadBegin;
12
+ static VALUE tpThreadEnd;
13
+ static VALUE debugProc;
14
+ static VALUE breakpoints;
15
+ static int knownBreakpoints;
16
+ static int firstLineEvent = 0;
17
+
18
+ static int match_line(VALUE next_file, int next_line, thread_reference_t *ptr)
19
+ {
20
+ if (rb_str_equal(next_file, ptr->prev_file) && next_line == ptr->prev_line) {
21
+ return 1;
22
+ }
23
+ return 0;
24
+ }
25
+
26
+ static int match_breakpoint(VALUE file, int line)
27
+ {
28
+ VALUE bps, b;
29
+ long len, i;
30
+
31
+ bps = rb_funcall(breakpoints, rb_intern("for"), 1, file);
32
+ len = rb_array_len(bps);
33
+ for (i = 0; i < len; i++)
34
+ {
35
+ b = rb_ary_entry(bps, i);
36
+ if (NUM2INT(rb_funcall(b, rb_intern("line"), 0)) == line)
37
+ {
38
+ return 1;
39
+ }
40
+ }
41
+ return 0;
42
+ }
43
+
44
+ static int match_step(thread_reference_t *ptr)
45
+ {
46
+ if (ptr->control == rb_intern("continue"))
47
+ {
48
+ return 0;
49
+ }
50
+ else if (ptr->control == rb_intern("next") && ptr->cursor >= ptr->depth)
51
+ {
52
+ return 1;
53
+ }
54
+ else if (ptr->control == rb_intern("step_in") && ptr->cursor < ptr->depth)
55
+ {
56
+ return 1;
57
+ }
58
+ else if (ptr->control == rb_intern("step_out") && ptr->cursor > ptr->depth)
59
+ {
60
+ return 1;
61
+ }
62
+ return 0;
63
+ }
64
+
65
+ static ID
66
+ monitor_debug(VALUE file, int line, VALUE tracepoint, thread_reference_t *ptr, ID event)
67
+ {
68
+ VALUE bind, bid, snapshot, result;
69
+
70
+ bind = rb_funcall(tracepoint, rb_intern("binding"), 0);
71
+ bid = rb_funcall(bind, rb_intern("object_id"), 0);
72
+ snapshot = rb_funcall(c_Snapshot, rb_intern("new"), 7,
73
+ LONG2NUM(ptr->id),
74
+ bid,
75
+ file,
76
+ INT2NUM(line),
77
+ Qnil,
78
+ ID2SYM(event),
79
+ INT2NUM(ptr->depth)
80
+ );
81
+ rb_io_flush(rb_stdout);
82
+ rb_io_flush(rb_stderr);
83
+ rb_funcall(debugProc, rb_intern("call"), 1, snapshot);
84
+ result = SYM2ID(rb_funcall(snapshot, rb_intern("control"), 0));
85
+ if (event != rb_intern("initialize"))
86
+ {
87
+ ptr->cursor = ptr->depth;
88
+ ptr->control = result;
89
+ }
90
+ ptr->prev_file = file;
91
+ ptr->prev_line = line;
92
+ return result;
93
+ }
94
+
95
+ static void
96
+ process_line_event(VALUE tracepoint, void *data)
97
+ {
98
+ VALUE ref, tp_file;
99
+ int tp_line;
100
+ thread_reference_t *ptr;
101
+ rb_trace_arg_t *tp;
102
+ int threadPaused;
103
+ ID dapEvent, result;
104
+
105
+ ref = thread_current_reference();
106
+ if (!RB_NIL_P(ref))
107
+ {
108
+ ptr = thread_reference_pointer(ref);
109
+ if (ptr->depth > 0 /*|| !firstLineEvent*/)
110
+ {
111
+ threadPaused = (ptr->control == rb_intern("pause"));
112
+ if (!firstLineEvent || threadPaused || knownBreakpoints || ptr->control != rb_intern("continue"))
113
+ {
114
+ tp = rb_tracearg_from_tracepoint(tracepoint);
115
+ tp_file = rb_tracearg_path(tp);
116
+ tp_line = NUM2INT(rb_tracearg_lineno(tp));
117
+
118
+ dapEvent = NULL;
119
+ if (!firstLineEvent)
120
+ {
121
+ dapEvent = rb_intern("initialize");
122
+ }
123
+ else if (threadPaused)
124
+ {
125
+ dapEvent = rb_intern("pause");
126
+ }
127
+ else if (match_step(ptr))
128
+ {
129
+ dapEvent = rb_intern("step");
130
+ }
131
+ else if (match_breakpoint(tp_file, tp_line))
132
+ {
133
+ dapEvent = rb_intern("breakpoint");
134
+ }
135
+ else if (ptr->control == rb_intern("entry"))
136
+ {
137
+ dapEvent = rb_intern("entry");
138
+ }
139
+ if (dapEvent)
140
+ {
141
+ result = monitor_debug(tp_file, tp_line, tracepoint, ptr, dapEvent);
142
+ if (dapEvent == rb_intern("initialize") && result == rb_intern("ready"))
143
+ {
144
+ firstLineEvent = 1;
145
+ ptr->control = rb_intern("entry");
146
+ process_line_event(tracepoint, data);
147
+ }
148
+ }
149
+ }
150
+ else
151
+ {
152
+ ptr->prev_file = Qnil;
153
+ ptr->prev_line = Qnil;
154
+ }
155
+ }
156
+ }
157
+ }
158
+
159
+ static void
160
+ process_call_event(VALUE tracepoint, void *data)
161
+ {
162
+ VALUE ref;
163
+ thread_reference_t *ptr;
164
+
165
+ ref = thread_current_reference();
166
+ if (!RB_NIL_P(ref))
167
+ {
168
+ ptr = thread_reference_pointer(ref);
169
+ ptr->depth++;
170
+ }
171
+ }
172
+
173
+ static void
174
+ process_return_event(VALUE tracepoint, void *data)
175
+ {
176
+ VALUE ref;
177
+ thread_reference_t *ptr;
178
+
179
+ ref = thread_current_reference();
180
+ if (!RB_NIL_P(ref))
181
+ {
182
+ ptr = thread_reference_pointer(ref);
183
+ ptr->depth--;
184
+ }
185
+ }
186
+
187
+ static void
188
+ process_thread_begin_event(VALUE tracepoint, void *data)
189
+ {
190
+ VALUE list, here, prev, ref;
191
+ thread_reference_t *ptr;
192
+
193
+ list = rb_funcall(rb_cThread, rb_intern("list"), 0);
194
+ here = rb_ary_pop(list);
195
+ if (!RB_NIL_P(here))
196
+ {
197
+ prev = rb_ary_pop(list);
198
+ {
199
+ if (!RB_NIL_P(prev))
200
+ {
201
+ ref = thread_reference(prev);
202
+ if (!RB_NIL_P(ref))
203
+ {
204
+ ref = thread_add_reference(here);
205
+ ptr = thread_reference_pointer(ref);
206
+ monitor_debug(
207
+ rb_funcall(tracepoint, rb_intern("path"), 0),
208
+ NUM2INT(rb_funcall(tracepoint, rb_intern("lineno"), 0)),
209
+ tracepoint,
210
+ ptr,
211
+ rb_intern("thread_begin")
212
+ );
213
+ }
214
+ }
215
+ }
216
+ }
217
+ }
218
+
219
+ static void
220
+ process_thread_end_event(VALUE tracepoint, void *data)
221
+ {
222
+ VALUE thr, ref;
223
+ thread_reference_t *ptr;
224
+
225
+ thr = rb_thread_current();
226
+ ref = thread_reference(thr);
227
+ if (!RB_NIL_P(ref))
228
+ {
229
+ ptr = thread_reference_pointer(ref);
230
+ monitor_debug(ptr->prev_file, ptr->prev_line, tracepoint, ptr, rb_intern("thread_end"));
231
+ thread_delete_reference(thr);
232
+ }
233
+ }
234
+
235
+ static VALUE
236
+ monitor_enable_s(VALUE self)
237
+ {
238
+ VALUE previous, ref;
239
+ thread_reference_t *ptr;
240
+
241
+ if (rb_block_given_p()) {
242
+ debugProc = rb_block_proc();
243
+ rb_global_variable(&debugProc);
244
+ } else {
245
+ rb_raise(rb_eArgError, "must be called with a block");
246
+ }
247
+
248
+ firstLineEvent = 0;
249
+
250
+ ref = thread_add_reference(rb_thread_current());
251
+ ptr = thread_reference_pointer(ref);
252
+ monitor_debug(
253
+ Qnil,
254
+ 0,
255
+ Qnil,
256
+ ptr,
257
+ rb_intern("thread_begin")
258
+ );
259
+
260
+ previous = rb_tracepoint_enabled_p(tpLine);
261
+ rb_tracepoint_enable(tpLine);
262
+ rb_tracepoint_enable(tpCall);
263
+ rb_tracepoint_enable(tpReturn);
264
+ rb_tracepoint_enable(tpThreadBegin);
265
+ rb_tracepoint_enable(tpThreadEnd);
266
+ return previous;
267
+ }
268
+
269
+ static VALUE
270
+ monitor_disable_s(VALUE self)
271
+ {
272
+ VALUE previous;
273
+
274
+ previous = rb_tracepoint_enabled_p(tpLine);
275
+ rb_tracepoint_disable(tpLine);
276
+ rb_tracepoint_disable(tpCall);
277
+ rb_tracepoint_disable(tpReturn);
278
+ rb_tracepoint_disable(tpThreadBegin);
279
+ rb_tracepoint_disable(tpThreadEnd);
280
+
281
+ return previous;
282
+ }
283
+
284
+ static VALUE
285
+ monitor_pause_s(VALUE self, VALUE id)
286
+ {
287
+ VALUE ref;
288
+ thread_reference_t *ptr;
289
+
290
+ ref = thread_reference_id(id);
291
+ if (!RB_NIL_P(ref))
292
+ {
293
+ ptr = thread_reference_pointer(ref);
294
+ ptr->control = rb_intern("pause");
295
+ }
296
+ }
297
+
298
+ static VALUE
299
+ monitor_know_breakpoints_s(VALUE self)
300
+ {
301
+ knownBreakpoints = (rb_funcall(breakpoints, rb_intern("empty?"), 0) == Qfalse) ? 1 : 0;
302
+ return Qnil;
303
+ }
304
+
305
+ void initialize_monitor(VALUE m_Readapt)
306
+ {
307
+ m_Monitor = rb_define_module_under(m_Readapt, "Monitor");
308
+ c_Snapshot = rb_define_class_under(m_Readapt, "Snapshot", rb_cObject);
309
+
310
+ initialize_threads();
311
+
312
+ rb_define_singleton_method(m_Monitor, "start", monitor_enable_s, 0);
313
+ rb_define_singleton_method(m_Monitor, "stop", monitor_disable_s, 0);
314
+ rb_define_singleton_method(m_Monitor, "know_breakpoints", monitor_know_breakpoints_s, 0);
315
+ rb_define_singleton_method(m_Monitor, "pause", monitor_pause_s, 1);
316
+
317
+ tpLine = rb_tracepoint_new(Qnil, RUBY_EVENT_LINE, process_line_event, NULL);
318
+ tpCall = rb_tracepoint_new(Qnil, RUBY_EVENT_CALL | RUBY_EVENT_B_CALL | RUBY_EVENT_CLASS, process_call_event, NULL);
319
+ tpReturn = rb_tracepoint_new(Qnil, RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN | RUBY_EVENT_END, process_return_event, NULL);
320
+ tpThreadBegin = rb_tracepoint_new(Qnil, RUBY_EVENT_THREAD_BEGIN, process_thread_begin_event, NULL);
321
+ tpThreadEnd = rb_tracepoint_new(Qnil, RUBY_EVENT_THREAD_END, process_thread_end_event, NULL);
322
+ debugProc = Qnil;
323
+ breakpoints = rb_funcall(m_Monitor, rb_intern("breakpoints"), 0);
324
+ knownBreakpoints = 0;
325
+
326
+ // Avoid garbage collection
327
+ rb_global_variable(&tpLine);
328
+ rb_global_variable(&tpCall);
329
+ rb_global_variable(&tpReturn);
330
+ rb_global_variable(&tpThreadBegin);
331
+ rb_global_variable(&tpThreadEnd);
332
+ }