rookout 0.1.0 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rookout/augs/actions/action_run_processor.rb +3 -3
  3. data/lib/rookout/augs/aug.rb +24 -90
  4. data/lib/rookout/augs/aug_factory.rb +24 -14
  5. data/lib/rookout/augs/aug_rate_limiter.rb +3 -3
  6. data/lib/rookout/augs/augs_manager.rb +3 -1
  7. data/lib/rookout/augs/conditions/condition.rb +4 -2
  8. data/lib/rookout/augs/locations/location.rb +75 -1
  9. data/lib/rookout/augs/locations/location_exception_handler.rb +22 -0
  10. data/lib/rookout/augs/locations/location_file_line.rb +16 -4
  11. data/lib/rookout/com_ws/agent_com_ws.rb +28 -33
  12. data/lib/rookout/com_ws/information.rb +1 -1
  13. data/lib/rookout/com_ws/output.rb +4 -10
  14. data/lib/rookout/com_ws/pinger.rb +36 -0
  15. data/lib/rookout/com_ws/websocket_client.rb +143 -0
  16. data/lib/rookout/commit.rb +3 -0
  17. data/lib/rookout/config.rb +1 -0
  18. data/lib/rookout/exceptions.rb +40 -0
  19. data/lib/rookout/interface.rb +41 -24
  20. data/lib/rookout/logger.rb +16 -2
  21. data/lib/rookout/processor/namespace_serializer.rb +1 -1
  22. data/lib/rookout/processor/namespaces/frame_namespace.rb +1 -0
  23. data/lib/rookout/processor/namespaces/namespace.rb +2 -2
  24. data/lib/rookout/processor/namespaces/noop_namespace.rb +1 -1
  25. data/lib/rookout/processor/namespaces/ruby_object_serializer.rb +4 -3
  26. data/lib/rookout/processor/operations/set_operation.rb +4 -1
  27. data/lib/rookout/processor/paths/arithmetic_path.rb +1 -1
  28. data/lib/rookout/processor/paths/canopy/markers.rb +5 -2
  29. data/lib/rookout/rookout_singleton.rb +4 -3
  30. data/lib/rookout/services/position.rb +73 -73
  31. data/lib/rookout/services/tracer.rb +8 -5
  32. data/lib/rookout/trigger_services.rb +2 -2
  33. data/lib/rookout/user_warnings.rb +2 -0
  34. data/lib/rookout/utils.rb +9 -0
  35. data/lib/rookout/version.rb +1 -2
  36. metadata +37 -32
@@ -11,6 +11,14 @@ module Rookout
11
11
  LOG_LEVELS = [:DEBUG, :INFO, :WARNING, :ERROR].freeze
12
12
 
13
13
  def initialize
14
+ # Detect unit tests
15
+ if ($PROGRAM_NAME.end_with?("minitest_runner.rb") ||
16
+ $PROGRAM_NAME.end_with?("tunit_or_minitest_in_folder_runner.rb")) &&
17
+ Dir.pwd.end_with?("ruby-sdk")
18
+ Config.logger_log_level = :DEBUG
19
+ Config.logger_log_to_stderr = true
20
+ end
21
+
14
22
  @verbosity = LOG_LEVELS.index(Config.logger_log_level) || LOG_LEVELS.index(:INFO)
15
23
 
16
24
  @output = nil
@@ -43,7 +51,6 @@ module Rookout
43
51
  end
44
52
 
45
53
  def exception message, exc
46
- # TODO: REVIEW HOW TO REPORT EXCEPTIONS
47
54
  error message, exc
48
55
  end
49
56
 
@@ -54,7 +61,14 @@ module Rookout
54
61
  @level = level
55
62
  @time = Time.new
56
63
  @message = message
57
- @formatted_message = message % arguments
64
+ @formatted_message = @message % arguments
65
+
66
+ arguments.each do |argument|
67
+ if argument.is_a? Exception
68
+ @formatted_message += "\n" + argument.message + "\n" + argument.backtrace.join("\n\t")
69
+ end
70
+ end
71
+
58
72
  @arguments = arguments
59
73
  set_caller
60
74
  end
@@ -17,7 +17,7 @@ module Rookout
17
17
  Logger.instance.exception message, e
18
18
 
19
19
  error = RookError.new e, message
20
- variant = error.dumps
20
+ variant.error_value = error.dumps
21
21
  end
22
22
  variant
23
23
  end
@@ -12,6 +12,7 @@ module Rookout
12
12
  end
13
13
 
14
14
  def read_attribute name
15
+ return RubyObjectNamespace.new @binding.receiver if name == "self"
15
16
  raise Exceptions::RookAttributeNotFound, name unless @binding.local_variable_defined? name
16
17
  RubyObjectNamespace.new @binding.local_variable_get name
17
18
  end
@@ -4,7 +4,7 @@ module Rookout
4
4
  class Namespace
5
5
  require_relative "../../exceptions"
6
6
  def call_method name, _args
7
- raise Exceptions::RookMethodNotFound, self.class.to_s, name
7
+ raise Exceptions::RookMethodNotFound.new(self.class.to_s, name)
8
8
  end
9
9
 
10
10
  def read_attribute name
@@ -12,7 +12,7 @@ module Rookout
12
12
  end
13
13
 
14
14
  def write_attribute name, _value
15
- raise Exceptions::RookWriteAttributeNotSupported, self.class.to_s, name
15
+ raise Exceptions::RookWriteAttributeNotSupported.new(self.class.to_s, name)
16
16
  end
17
17
 
18
18
  def read_key key
@@ -6,7 +6,7 @@ module Rookout
6
6
 
7
7
  require_relative "../../protobuf/variant_pb"
8
8
 
9
- class RubyUtilsNamespace < Namespace
9
+ class NoopNamespace < Namespace
10
10
  def initialize
11
11
  super
12
12
  end
@@ -17,7 +17,7 @@ module Rookout
17
17
  Logger.instance.exception message, e
18
18
 
19
19
  error = RookError.new e, message
20
- variant = error.dumps
20
+ variant.error_value = error.dumps
21
21
  end
22
22
  variant
23
23
  end
@@ -29,7 +29,7 @@ module Rookout
29
29
 
30
30
  if obj.nil?
31
31
  dump_nil variant
32
- elsif obj.is_a?(Numeric) || obj == true || obj == false
32
+ elsif obj.is_a?(Numeric) || obj.is_a?(TrueClass) || obj.is_a?(FalseClass)
33
33
  dump_numeric obj, variant
34
34
  elsif obj.is_a?(String) || obj.is_a?(Symbol)
35
35
  dump_string obj, variant, config
@@ -104,7 +104,7 @@ module Rookout
104
104
  variant.complex_value = Com::Rookout::Variant::Complex.new real: obj.real.to_f,
105
105
  imaginary: obj.imaginary.to_f
106
106
  else
107
- raise Exceptions.RookClassCannotBeSerialized, obj.class, "Unknown Numeric Type"
107
+ raise Exceptions::RookClassCannotBeSerialized.new(obj.class, "Unknown Numeric Type")
108
108
  end
109
109
  # TODO: ADD SUPPORT FOR RATIONALS
110
110
  end
@@ -123,6 +123,7 @@ module Rookout
123
123
  end
124
124
 
125
125
  def dump_string obj, variant, config
126
+ obj = obj.to_s
126
127
  if obj.length > config.max_string
127
128
  final_obj = obj[0...config.max_string]
128
129
  else
@@ -3,6 +3,8 @@ module Rookout
3
3
  module Operations
4
4
  require_relative "operation"
5
5
 
6
+ require_relative "../namespaces/ruby_object_namespace"
7
+
6
8
  require_relative "../rook_error"
7
9
  require_relative "../../logger"
8
10
  require_relative "../../user_warnings"
@@ -29,7 +31,8 @@ module Rookout
29
31
  @paths.each do |dest_path, source_path|
30
32
  begin
31
33
  value = source_path.read_from namespace
32
- if value.is_a?(RubyObjectNamespace) && value.dump_config == Namespaces::OBJECT_DUMP_CONFIG_DEFAULT
34
+ if value.is_a?(Namespaces::RubyObjectNamespace) &&
35
+ value.dump_config == Namespaces::OBJECT_DUMP_CONFIG_DEFAULT
33
36
  value.tailor_limits!
34
37
  end
35
38
 
@@ -71,7 +71,7 @@ module Rookout
71
71
 
72
72
  if result.is_a? Canopy::ToolExceptionMarker
73
73
  raise result.obj if result.obj.is_a? Exceptions::ToolException
74
- raise Exceptions::RookInvalidArithmeticPath, @raw_path, result.obj
74
+ raise Exceptions::RookInvalidArithmeticPath.new(@raw_path, result.obj)
75
75
  end
76
76
 
77
77
  res = result.obj
@@ -4,6 +4,9 @@ module Rookout
4
4
  module Canopy
5
5
  require_relative "consts"
6
6
 
7
+ require_relative "../../../exceptions"
8
+ require_relative "../../namespaces/container_namespace"
9
+
7
10
  class Marker; end
8
11
 
9
12
  class Operation < Marker
@@ -12,7 +15,7 @@ module Rookout
12
15
  end
13
16
 
14
17
  def write _namespace, _value
15
- raise Exception.RookOperationReadOnly, self.class.to_s
18
+ raise Exception::RookOperationReadOnly, self.class.to_s
16
19
  end
17
20
  end
18
21
 
@@ -36,7 +39,7 @@ module Rookout
36
39
  namespace.read_attribute @name
37
40
  rescue Exceptions::RookAttributeNotFound
38
41
  raise unless create
39
- namespace.write_attribute name, ContainerNamespace.new
42
+ namespace.write_attribute @name, Namespaces::ContainerNamespace.new
40
43
  end
41
44
 
42
45
  def write namespace, value
@@ -40,7 +40,7 @@ module Rookout
40
40
  @services_started = false
41
41
  end
42
42
 
43
- def connect token, host, port, proxy, labels, async_start, _fork
43
+ def connect token: nil, host: nil, port: nil, proxy: nil, labels: [], async_start: false, **_
44
44
  raise Exceptions::RookInterfaceException, "Multiple connection attempts not supported!" unless @agent_com.nil?
45
45
 
46
46
  start_trigger_services
@@ -48,6 +48,7 @@ module Rookout
48
48
  Logger.instance.debug "Initiating AgentCom-\t#{host}:#{port}"
49
49
 
50
50
  @agent_com = ComWs::AgentComWs.new @output, host, port, proxy, token, labels
51
+ @output.agent_com = @agent_com
51
52
  @command_handler = ComWs::CommandHandler.new @agent_com, @aug_manager
52
53
 
53
54
  @agent_com.connect
@@ -65,8 +66,8 @@ module Rookout
65
66
  private
66
67
 
67
68
  def check_version_supported
68
- raise Exceptions:: RookVersionNotSupported, "platform", RUBY_ENGINE unless RUBY_ENGINE == "ruby"
69
- raise Exceptions::RookVersionNotSupported, "version", RUBY_VERSION unless
69
+ raise Exceptions:: RookVersionNotSupported.new("platform", RUBY_ENGINE) unless RUBY_ENGINE == "ruby"
70
+ raise Exceptions::RookVersionNotSupported.new("version", RUBY_VERSION) unless
70
71
  RUBY_VERSION.start_with?("2.7") || RUBY_VERSION.start_with?("2.6")
71
72
  end
72
73
  end
@@ -1,5 +1,7 @@
1
1
  module Rookout
2
2
  module Services
3
+ require "zlib"
4
+
3
5
  require_relative "../logger"
4
6
  require_relative "../exceptions"
5
7
 
@@ -16,10 +18,16 @@ module Rookout
16
18
  def initialize tracer
17
19
  @tracer = tracer
18
20
  @augs = {}
21
+ @iseqs = []
19
22
  @trace_point = TracePoint.new :script_compiled do |tp|
20
23
  begin
21
24
  begin
22
- evaluate_script tp.instruction_sequence
25
+ iseq = tp.instruction_sequence
26
+ # Ignore script without sources
27
+ if iseq.absolute_path
28
+ @iseqs << iseq
29
+ evaluate_script iseq
30
+ end
23
31
  rescue Exception => e
24
32
  Logger.instance.exception "Exception while evaluating script", e
25
33
  end
@@ -30,22 +38,18 @@ module Rookout
30
38
  @trace_point.enable
31
39
  end
32
40
 
33
- def add_aug location, aug
34
- positions = resolve_positions location, aug
35
- if positions.empty?
36
- @augs[aug.id] = [location, aug]
37
- else
38
- @tracer.add_breakpoint_aug positions, aug
39
- end
41
+ def add_aug location
42
+ positions = evaluate_all_scripts_to_location location
43
+ @tracer.add_breakpoint_aug positions, location unless positions.empty?
44
+ @augs[location.id] = location
40
45
  end
41
46
 
42
47
  def remove_aug aug_id
43
- pair = @augs[aug_id]
44
- return if pair.nil?
48
+ location = @augs[aug_id]
49
+ return if location.nil?
45
50
 
46
51
  @augs.delete [aug_id]
47
- aug = pair[1]
48
- aug.notify_removed
52
+ location.notify_removed
49
53
  end
50
54
 
51
55
  def clear_augs
@@ -64,88 +68,47 @@ module Rookout
64
68
  private
65
69
 
66
70
  #########################################################################################
67
- # Dynamic Loading Section
71
+ # ISEQ comparison Section
68
72
  def evaluate_script iseq
69
73
  filename = iseq.absolute_path
70
74
  return if filename.nil?
71
75
 
72
- @augs.each do |pair|
73
- location = pair[0]
76
+ @augs.each_value do |location|
77
+ position = evaluate_script_to_location iseq, filename, location
78
+ next if position.nil?
74
79
 
75
- if exact_match? location, filename
76
- # TODO: CHECK FILE AND LINE HASH COMPARED TO AUG
77
- position = PositionMarker.new iseq.absolute_path, location.lineno, iseq
78
- @tracer.add_breakpoint_aug [position], pair[1]
79
- elsif suggested_match? location, filename
80
- warning = Exceptions::RookSourceFilePathSuggestion.new location.filename, filename
81
- pair[1].notify_warning warning
82
- end
80
+ @tracer.add_breakpoint_aug [position], location
83
81
  end
84
82
  end
85
83
 
86
- #########################################################################################
87
- # Enumerating Section
88
- def resolve_positions location, aug
89
- # TODO: ADD HASHES AND SUGGESTIONS TO THIS FLOW
84
+ def evaluate_all_scripts_to_location location
90
85
  positions = []
86
+ @iseqs.each do |iseq|
87
+ position = evaluate_script_to_location iseq, iseq.absolute_path, location
88
+ next if position.nil?
91
89
 
92
- ObjectSpace.each_object Module do |mod|
93
- positions += scan_module_or_class mod, location, aug
94
- end
95
-
96
- ObjectSpace.each_object Class do |klass|
97
- positions += scan_module_or_class klass, location, aug
90
+ positions << position
98
91
  end
99
92
 
100
93
  positions
101
94
  end
102
95
 
103
- def scan_module_or_class mod, location, aug
104
- positions = []
105
-
106
- mod.instance_methods(false).each do |method_name|
107
- method = mod.instance_method method_name
108
- next unless method_match? method, location, aug
109
-
110
- position = PositionMarker.new location.lineno, method
111
- positions.push position
112
- end
113
-
114
- mod.private_instance_methods(false).each do |method_name|
115
- method = mod.instance_method method_name
116
- next unless method_match? method, location, aug
96
+ def evaluate_script_to_location iseq, filename, location
97
+ if exact_match? location.filename, filename
98
+ lineno = find_updated_line filename, location
99
+ return if lineno == -1
117
100
 
118
- position = PositionMarker.new location.lineno, method
119
- positions.push position
120
- end
121
-
122
- positions
123
- end
124
-
125
- def method_match? method, location, aug
126
- return false unless method.source_location
127
- filename = method.source_location[0]
128
-
129
- if exact_match? location, filename
130
- disasm_header = "== disasm: #<ISeq:#{method.name}@#{location.filename}:"
131
- line_in_disasm = "(#{location.lineno.to_s.rjust 4})"
132
- text = RubyVM::InstructionSequence.disasm method
133
- return false if text.nil?
134
-
135
- text.start_with?(disasm_header) && text.include?(line_in_disasm)
136
- else
137
- if suggested_match? location, filename
138
- warning = Exceptions::RookSourceFilePathSuggestion.new location.filename, filename
139
- aug.notify_warning warning
140
- end
141
- false
101
+ PositionMarker.new lineno, iseq
102
+ elsif suggested_match? location, filename
103
+ warning = Exceptions::RookSourceFilePathSuggestion.new location.filename, filename
104
+ location.notify_warning warning
142
105
  end
143
106
  end
144
107
 
145
108
  #########################################################################################
146
109
  # Utils
147
- def exact_match? location, filename
148
- filename.end_with? location.filename
110
+ def exact_match? location_filename, script_filename
111
+ script_filename.end_with? location_filename
149
112
  end
150
113
 
151
114
  def suggested_match? location, filename
@@ -158,6 +121,43 @@ module Rookout
158
121
  content.gsub!(/(?:\r\n|\r|\n)/, "\n")
159
122
  Digest::SHA2.new(256).hexdigest content
160
123
  end
124
+
125
+ def find_updated_line filename, location
126
+ return location.lineno if location.line_crc.nil?
127
+
128
+ lines = File.readlines filename, chomp: true
129
+ line_crc32 = lines.length >= location.lineno ? crc_line(lines[location.lineno - 1]) : nil
130
+ return location.lineno if location.line_crc == line_crc32
131
+
132
+ if location.line_unique
133
+ first_line = nil
134
+ second_found = false
135
+
136
+ lines.each_with_index do |line, index|
137
+ if crc_line(line) == location.line_crc
138
+ if first_line.nil?
139
+ first_line = index
140
+ else
141
+ second_found = true
142
+ break
143
+ end
144
+ end
145
+ end
146
+
147
+ if first_line && !second_found
148
+ updated_line = first_line + 1
149
+ location.notify_warning Exceptions::RookLineMoved.new(filename, location.lineno, updated_line)
150
+ return updated_line
151
+ end
152
+ end
153
+
154
+ location.notify_error Exceptions::RookCrcMismatchException.new(filename, location.line_crc, line_crc32)
155
+ -1
156
+ end
157
+
158
+ def crc_line line
159
+ (Zlib.crc32(line) & 0xffffffff).to_s 16
160
+ end
161
161
  end
162
162
  end
163
163
  end
@@ -1,9 +1,11 @@
1
1
  module Rookout
2
2
  module Services
3
- require "binding_of_caller"
4
- include BindingOfCaller::BindingExtensions
3
+ require_relative "../logger"
5
4
 
6
5
  class Tracer
6
+ require "binding_of_caller"
7
+ include BindingOfCaller::BindingExtensions
8
+
7
9
  def initialize
8
10
  @trace_points = {}
9
11
  @augs = {}
@@ -19,7 +21,7 @@ module Rookout
19
21
  begin
20
22
  trace_point.enable target: position.method, target_line: position.lineno
21
23
  rescue ArgumentError => e
22
- raise Exceptions::RookSetTracepointFailed, position.lineno, e
24
+ raise Exceptions::RookSetTracepointFailed.new(position.lineno, e)
23
25
  end
24
26
 
25
27
  # We add and remove a dummy trace point to re-align the tracing mechanism as a result of some bug in adding
@@ -69,9 +71,10 @@ module Rookout
69
71
  TracePoint.new :line do
70
72
  begin
71
73
  begin
72
- aug.execute BindingOfCaller::BindingExtensions.callers, nil
74
+ # TODO: consider delaying callers (get bindings) until we actually need them in Aug.rb
75
+ aug.execute callers, nil
73
76
  rescue Exception => e
74
- Logger.instance.exception "Exception while evaluating script", e
77
+ Logger.instance.exception "Exception calling aug", e
75
78
  end
76
79
  rescue
77
80
  # Don't leak any exception from here
@@ -17,8 +17,8 @@ module Rookout
17
17
  @services.each_value { |service| service.remove_aug aug_id }
18
18
  end
19
19
 
20
- def clear_augs aug_id
21
- @services.each_value { |service| service.clear_augs aug_id }
20
+ def clear_augs
21
+ @services.each_value(&:clear_augs)
22
22
  end
23
23
 
24
24
  def start