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
@@ -0,0 +1,20 @@
1
+ require 'pathname'
2
+
3
+ module Readapt
4
+ module Finder
5
+ module_function
6
+
7
+ def find program
8
+ return program if File.exist?(program)
9
+ which(program) || raise(LoadError, "#{program} is not a valid Ruby file or program")
10
+ end
11
+
12
+ def which program
13
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
14
+ exe = File.join(path, program)
15
+ return exe if File.file?(exe)
16
+ end
17
+ nil
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ class Frame
5
+ attr_reader :location
6
+
7
+ def initialize location, binding_id
8
+ @location = location
9
+ @binding = ObjectSpace._id2ref(binding_id)
10
+ end
11
+
12
+ def local_id
13
+ @binding.object_id
14
+ end
15
+
16
+ def locals
17
+ return [] if @binding.nil?
18
+ result = []
19
+ @binding.local_variables.each do |sym|
20
+ var = @binding.local_variable_get(sym)
21
+ result.push Variable.new(sym, var)
22
+ end
23
+ if @binding.receiver != TOPLEVEL_BINDING.receiver
24
+ result.push Variable.new(:self, @binding.receiver)
25
+ end
26
+ result
27
+ end
28
+
29
+ def variables
30
+ return [] if @binding.nil?
31
+ result = []
32
+ @binding.local_variables.each do |sym|
33
+ var = @binding.local_variable_get(sym)
34
+ result.push Variable.new(sym, var)
35
+ end
36
+ if @binding.receiver != TOPLEVEL_BINDING.receiver
37
+ @binding.instance_variables.each do |sym|
38
+ var = @binding.receiver.instance_variable_get(sym)
39
+ result.push Variable.new(sym, var)
40
+ end
41
+ @binding.class.class_variables.each do |sym|
42
+ var = @binding.receiver.class.class_variable_get(sym)
43
+ result.push Variable.new(sym, var)
44
+ end
45
+ end
46
+ result
47
+ end
48
+
49
+ def local sym
50
+ return @binding.receiver if sym == :self
51
+ @binding.local_variable_get sym
52
+ end
53
+
54
+ def globals
55
+ global_variables
56
+ end
57
+
58
+ def global sym
59
+ global sym
60
+ end
61
+
62
+ def evaluate code
63
+ @binding.eval code
64
+ end
65
+
66
+ NULL_FRAME = Frame.new(nil, nil.object_id)
67
+ end
68
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ # A simple file/line reference.
5
+ #
6
+ class Location
7
+ # @return [String]
8
+ attr_reader :file
9
+
10
+ # @return [Integer]
11
+ attr_reader :line
12
+
13
+ # @param file [String]
14
+ # @param line [Integer]
15
+ def initialize file, line
16
+ @file = file
17
+ @line = line
18
+ end
19
+
20
+ def match? other
21
+ return false unless other.is_a?(Location)
22
+ file == other.file && line == other.line
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'readapt/message/base'
4
+ require 'readapt/message/initialize'
5
+ require 'readapt/message/launch'
6
+ require 'readapt/message/set_breakpoints'
7
+ require 'readapt/message/set_exception_breakpoints'
8
+ require 'readapt/message/configuration_done'
9
+ require 'readapt/message/threads'
10
+ require 'readapt/message/stack_trace'
11
+ require 'readapt/message/scopes'
12
+ require 'readapt/message/continue'
13
+ require 'readapt/message/variables'
14
+ require 'readapt/message/next'
15
+ require 'readapt/message/step_in'
16
+ require 'readapt/message/step_out'
17
+ require 'readapt/message/disconnect'
18
+ require 'readapt/message/attach'
19
+ require 'readapt/message/pause'
20
+
21
+ module Readapt
22
+ module Message
23
+ @@messages = {}
24
+ @@seq = 0
25
+
26
+ def self.register name, klass
27
+ @@messages[name] = klass
28
+ end
29
+
30
+ def self.process arguments, debugger
31
+ message = @@messages[arguments['command']].new(arguments['arguments'], debugger)
32
+ message.run
33
+ message
34
+ rescue Exception => e
35
+ STDERR.puts "Skipping error: #{e.message}"
36
+ STDERR.puts "Received: #{arguments}"
37
+ STDERR.puts e.backtrace
38
+ end
39
+
40
+ register 'initialize', Message::Initialize
41
+ register 'attach', Message::Attach
42
+ register 'launch', Message::Launch
43
+ register 'setBreakpoints', Message::SetBreakpoints
44
+ register 'setExceptionBreakpoints', Message::SetExceptionBreakpoints
45
+ register 'configurationDone', Message::ConfigurationDone
46
+ register 'threads', Message::Threads
47
+ register 'stackTrace', Message::StackTrace
48
+ register 'scopes', Message::Scopes
49
+ register 'continue', Message::Continue
50
+ register 'variables', Message::Variables
51
+ register 'next', Message::Next
52
+ register 'stepIn', Message::StepIn
53
+ register 'stepOut', Message::StepOut
54
+ register 'disconnect', Message::Disconnect
55
+ register 'pause', Message::Pause
56
+ end
57
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Attach < Base
6
+ def run
7
+ debugger.config arguments, :attach
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Base
6
+ # @return [Hash]
7
+ attr_reader :arguments
8
+
9
+ # @return [Debugger]
10
+ attr_reader :debugger
11
+
12
+ def initialize arguments, debugger
13
+ @arguments = arguments
14
+ @debugger = debugger
15
+ end
16
+
17
+ def run; end
18
+
19
+ def body
20
+ @body ||= {}
21
+ end
22
+
23
+ def set_body hash
24
+ @body = hash
25
+ end
26
+
27
+ def self.run arguments, debugger
28
+ new(arguments, debugger).run
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class ConfigurationDone < Base
6
+ def run
7
+ debugger.start
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Continue < Base
6
+ def run
7
+ thread = debugger.thread(arguments['threadId'])
8
+ thread.control = :continue
9
+ set_body({
10
+ allThreadsContinued: false
11
+ })
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Disconnect < Base
6
+ def run
7
+ # HACK: Wait a moment to make sure the output is flushed
8
+ # @todo Find a better way
9
+ sleep 1
10
+ debugger.disconnect
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Initialize < Base
6
+ def run
7
+ set_body({
8
+ supportsConfigurationDoneRequest: true
9
+ })
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Launch < Base
6
+ def run
7
+ debugger.config arguments, :launch
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Next < Base
6
+ def run
7
+ thread = debugger.thread(arguments['threadId'])
8
+ thread.control = :next
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Pause < Base
6
+ def run
7
+ Monitor.pause arguments['threadId']
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Scopes < Base
6
+ def run
7
+ frame = debugger.frame(arguments['frameId'])
8
+ set_body({
9
+ scopes: [
10
+ {
11
+ name: 'Local',
12
+ variablesReference: frame.local_id,
13
+ expensive: false
14
+ },
15
+ {
16
+ name: 'Global',
17
+ variablesReference: TOPLEVEL_BINDING.receiver.object_id,
18
+ expensive: true
19
+ }
20
+ ]
21
+ })
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class SetBreakpoints < Base
6
+ def run
7
+ path = Readapt.normalize_path(arguments['source']['path'])
8
+ Monitor.breakpoints.clear path
9
+ result = arguments['lines'].map do |l|
10
+ # @todo Assuming breakpoints are verified
11
+ Breakpoint.new(path, l, true)
12
+ end
13
+ Monitor.breakpoints.concat result
14
+ set_body({
15
+ breakpoints: result.map { |bp|
16
+ {
17
+ verified: bp.verified,
18
+ source: arguments['source'],
19
+ line: bp.line
20
+ }
21
+ }
22
+ })
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class SetExceptionBreakpoints < Base
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class StackTrace < Base
6
+ def run
7
+ frames = debugger.thread(arguments['threadId']).frames
8
+ set_body({
9
+ stackFrames: frames.map do |frm|
10
+ {
11
+ name: "#{File.basename(frm.location.file)}:#{frm.location.line}",
12
+ source: {
13
+ name: File.basename(frm.location.file),
14
+ path: frm.location.file
15
+ },
16
+ id: frm.local_id,
17
+ line: frm.location.line,
18
+ column: 0
19
+ }
20
+ end,
21
+ totalFrames: frames.length
22
+ })
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class StepIn < Base
6
+ def run
7
+ debugger.thread(arguments['threadId']).control = :step_in
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class StepOut < Base
6
+ def run
7
+ debugger.thread(arguments['threadId']).control = :step_out
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Threads < Base
6
+ def run
7
+ set_body({
8
+ threads: debugger.threads.map do |thr|
9
+ {
10
+ id: thr.id,
11
+ name: thr.name
12
+ }
13
+ end
14
+ })
15
+ end
16
+ end
17
+ end
18
+ end