ruby-production-breakpoints 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 393332e205830e98423e4e80256b91d0a5619b2459dade719e22e9a90a507b34
4
- data.tar.gz: 255897924c095642d81a0efbfb4cb98acf384f99bc37d3ccc6b699729682410a
3
+ metadata.gz: c667c558395ef7da2130c3085956a438366a9ae49c9531b97ab08f994a90e157
4
+ data.tar.gz: 946c38475edf95386ac832e82815e5a5cb0efeb1a7f1d9a05300939dbc6fab94
5
5
  SHA512:
6
- metadata.gz: 65d5ef0836703f22a47344931525028b506d74f396d1185c7213e4db6d426cf3c33ba0c873474abd486bbb5e287976f4c7ec3a0006c22975ac93fe613e43be32
7
- data.tar.gz: f37633bf6107dfac1eccfe872069012af57ab1c85ce05a4211aa22dfff5ec5d47333dae2d5327f6ab1f85291d06fb2284a4ae56a20aa7c47df08ccfdadea340b
6
+ metadata.gz: 0146b4b990c9a9ac018eb4b3dc365891cfb9b55b0b8b9fc902ebfb4bb9ab50eeb548e3536d9a02d5ffd5568eb5c47dfb2d735f5c6339ee0153ae1f3412cf54c8
7
+ data.tar.gz: 4ed8781d90ea4351d1ff980726aac868198e46bbadcb820c88de0b6057464d8a255f9ba9a010919049e6ab8c746e0274dfb5a85828d93f3c9f1d0100a9681c05
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'unmixer'
4
+ using Unmixer
5
+
6
+ module ProductionBreakpoints
7
+ module Breakpoints
8
+ class Base
9
+ TRACEPOINT_TYPES = [].freeze
10
+
11
+ attr_reader :provider_name, :name, :tracepoint
12
+
13
+ def initialize(source_file, start_line, end_line, trace_id: 1)
14
+ @injector_module = nil
15
+ @source_file = source_file
16
+ @start_line = start_line
17
+ @end_line = end_line
18
+ @trace_id = trace_id
19
+ @method = self.class.name.split('::').last.downcase
20
+ @parser = ProductionBreakpoints::Parser.new(@source_file)
21
+ @node = @parser.find_definition_node(@start_line, @end_line)
22
+ @method_override = ProductionBreakpoints::MethodOverride.new(@parser, start_line, end_line)
23
+ @ns = Object.const_get(@parser.find_definition_namespace(@node)) # FIXME: error handling, if not found
24
+ @provider_name = File.basename(@source_file).gsub('.', '_')
25
+ @name = "#{@method}_#{@trace_id}"
26
+ @tracepoint = StaticTracing::Tracepoint.new(@provider_name, @name, *self.class.const_get('TRACEPOINT_TYPES'))
27
+ end
28
+
29
+ def install
30
+ @injector_module = build_redefined_definition_module(@node)
31
+ @ns.prepend(@injector_module)
32
+ end
33
+
34
+ # FIXME: saftey if already uninstalled
35
+ def uninstall
36
+ @ns.instance_eval { unprepend(@injector_module) }
37
+ @injector_module = nil
38
+ end
39
+
40
+ def load
41
+ @tracepoint.provider.enable
42
+ end
43
+
44
+ def unload
45
+ @tracepoint.provider.disable
46
+ end
47
+
48
+ # Allows for specific handling of the selected lines
49
+ def handle(caller_binding)
50
+ eval(@method_override.handler_src.join, caller_binding)
51
+ end
52
+
53
+ def resume(caller_binding)
54
+ eval(@method_override.resume_src.join, caller_binding) if @method_override.resume_src
55
+ end
56
+
57
+ private
58
+
59
+ # A custom module we'll prepend in order to override
60
+ # us to keep the expected binding for the wrapped code, and remainder of the method
61
+ def build_redefined_definition_module(node)
62
+ # This is the metaprogramming to inject our breakpoint handle
63
+ handler = "ProductionBreakpoints.installed_breakpoints[:#{@trace_id}].handle(Kernel.binding)"
64
+
65
+ # This injects our handler at the end of the original source code
66
+ injected = @parser.inject_metaprogramming_handlers(handler, node.first_lineno, node.last_lineno)
67
+ # ProductionBreakpoints.config.logger.debug(injected)
68
+ Module.new { module_eval { eval(injected); eval('def production_breakpoint_enabled?; true; end;') } }
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ProductionBreakpoints
4
+ module Breakpoints
5
+ # Inspect result of the last evaluated expression
6
+ class Inspect < Base
7
+ TRACEPOINT_TYPES = [String].freeze
8
+
9
+ def handle(caller_binding, &block)
10
+ return super(caller_binding, &block) unless @tracepoint.enabled?
11
+
12
+ val = super(caller_binding, &block)
13
+ @tracepoint.fire(val.inspect)
14
+
15
+ resume(caller_binding, &block) || val
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ProductionBreakpoints
4
+ module Breakpoints
5
+ # Exposes nanosecond the latency of executing the selected lines
6
+ class Latency < Base # FIXME: refactor a bunch of these idioms into Base
7
+ TRACEPOINT_TYPES = [Integer].freeze
8
+
9
+ def handle(caller_binding, &block)
10
+ return super(caller_binding, &block) unless @tracepoint.enabled?
11
+
12
+ start_time = StaticTracing.nsec
13
+ val = super(caller_binding, &block)
14
+ duration = StaticTracing.nsec - start_time
15
+ @tracepoint.fire(duration)
16
+ resume(caller_binding, &block) || val
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ProductionBreakpoints
4
+ module Breakpoints
5
+ # Show local variables and their values
6
+ class Locals < Base # FIXME: refactor a bunch of these idioms into Base
7
+ TRACEPOINT_TYPES = [String].freeze
8
+
9
+ def handle(caller_binding, &block)
10
+ return super(caller_binding, &block) unless @tracepoint.enabled?
11
+
12
+ val = super(caller_binding, &block)
13
+ locals = caller_binding.local_variables
14
+ locals.delete(:local_bind)
15
+ vals = locals.map { |v| [v, caller_binding.local_variable_get(v)] }.to_h
16
+ @tracepoint.fire(vals.to_json)
17
+ resume(caller_binding, &block) || val
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,74 @@
1
+ # # frozen_string_literal: true
2
+
3
+ # module ProductionBreakpoints
4
+ # module Breakpoints
5
+ # # Show local variables and their values
6
+ # class Ustack < Base
7
+ # TRACEPOINT_TYPES = [String, String, String,
8
+ # String, String, String].freeze
9
+ # MAX_STACK_STR_SIZE = MAX_USDT_STR_SIZE * TRACEPOINT_TYPES.size
10
+
11
+ # def initialize(*args, &block)
12
+ # super(*args, &block)
13
+ # get_ruby_stack_str = <<-EOS
14
+ # caller.map { |l| l.split(":in ") }.to_h
15
+ # EOS
16
+ # @handler_iseq = RubyVM::InstructionSequence.compile(get_ruby_stack_str)
17
+ # end
18
+
19
+ # def handle(vm_tracepoint)
20
+ # return unless @tracepoint.enabled?
21
+
22
+ # stack_map = @handler_iseq.eval(vm_tracepoint.binding)
23
+
24
+ # shortened_map = {}
25
+ # stack_map.each do |k,v|
26
+ # newkey = k
27
+ # #if k.include?('gems')
28
+ # # newkey = k[k.rindex('gems')..-1].split(File::SEPARATOR)[1..-1]
29
+ # # .join(File::SEPARATOR)
30
+ # #elsif k.include?('lib')
31
+ # # newkey = k[k.rindex('lib')..-1].split(File::SEPARATOR)[1..-1]
32
+ # # .join(File::SEPARATOR)
33
+ # #end
34
+ # # FIXME lots of context is lost this way, as the above methods
35
+ # # consistently exceed the max size.
36
+ # # Need to find a more optimal way to shorten the stack here, to
37
+ # # pack it into the 1200 bytes available
38
+ # newkey = File.basename(k)
39
+ # shortened_map[newkey] = v
40
+ # end
41
+ # # ProductionBreakpoints.logger.debug(shortened_map.inspect)
42
+
43
+ # stack_str = shortened_map.to_json
44
+
45
+ # if stack_str.size > (MAX_STACK_STR_SIZE)
46
+ # ProductionBreakpoints.logger.error("Stack exceeds #{MAX_STACK_STR_SIZE}")
47
+ # # Truncate because i'm lazy
48
+ # stack_str = stack_str[0..MAX_STACK_STR_SIZE]
49
+ # end
50
+
51
+ # slices = stack_str.chars.each_slice(MAX_USDT_STR_SIZE).map(&:join)
52
+
53
+ # case slices.size
54
+
55
+ # when 1
56
+ # @tracepoint.fire(slices[0], "", "", "", "", "")
57
+ # when 2
58
+ # @tracepoint.fire(slices[0], slices[1], "", "", "", "")
59
+ # when 3
60
+ # @tracepoint.fire(slices[0], slices[1], slices[2], "", "", "")
61
+ # when 4
62
+ # @tracepoint.fire(slices[0], slices[1], slices[2], slices[3], "", "")
63
+ # when 5
64
+ # @tracepoint.fire(slices[0], slices[1], slices[2], slices[3],
65
+ # slices[4], "")
66
+ # when 6
67
+ # @tracepoint.fire(slices[0], slices[1], slices[2], slices[3],
68
+ # slices[4], slices[5])
69
+
70
+ # end
71
+ # end
72
+ # end
73
+ # end
74
+ # end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ruby-production-breakpoints/breakpoints/base'
4
+ require 'ruby-production-breakpoints/breakpoints/latency'
5
+ require 'ruby-production-breakpoints/breakpoints/inspect'
6
+ require 'ruby-production-breakpoints/breakpoints/locals'
7
+ require 'ruby-production-breakpoints/breakpoints/ustack'
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module ProductionBreakpoints
6
+ class Configuration
7
+ # Modes of operation for tracers
8
+ module Modes
9
+ ON = 'ON'
10
+ OFF = 'OFF'
11
+ SIGNAL = 'SIGNAL'
12
+
13
+ module SIGNALS
14
+ SIGURG = 'URG'
15
+ end
16
+ end
17
+
18
+ attr_reader :mode, :signal, :configured_breakpoints, :logger
19
+ attr_accessor :path
20
+
21
+ # A new configuration instance
22
+ def initialize
23
+ @mode = Modes::SIGNAL
24
+ @signal = Modes::SIGNALS::SIGURG
25
+ @logger = Logger.new(STDERR)
26
+ end
27
+
28
+ def finish!
29
+ if File.exist?(path)
30
+ @configured_breakpoints = JSON.load(File.read(path))
31
+ enable_trap
32
+ else
33
+ logger.error("Config file #{path} not found")
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ # Disables trap handler
40
+ def disable_trap
41
+ Signal.trap(@signal, 'DEFAULT')
42
+ end
43
+
44
+ # Enables a new trap handler
45
+ def enable_trap
46
+ # ProductionBreakpoints.config.logger.debug("trap handler enabled for #{@signal}")
47
+ Signal.trap(@signal) { ProductionBreakpoints.sync! }
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,5 @@
1
+
2
+ # frozen_string_literal: true
3
+
4
+ class NotConfiguredError < StandardError
5
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ProductionBreakpoints
4
+ # Extract valueable parts of the method
5
+ class MethodOverride
6
+ def initialize(parser, start_line, end_line)
7
+ @parser = parser
8
+ @source_lines = parser.source_lines
9
+ @node = parser.find_definition_node(start_line, end_line)
10
+ @start_line = start_line
11
+ @end_line = end_line
12
+ end
13
+
14
+ def unmodified_src
15
+ return if @start_line - @node.first_lineno <= 1 # if smaller or equal to one that means we are at the beginning of the method
16
+
17
+ @source_lines[(@node.first_lineno)..(@start_line - 2)]
18
+ end
19
+
20
+ def handler_src
21
+ @source_lines[@start_line - 1..@end_line - 1]
22
+ end
23
+
24
+ def resume_src
25
+ return if @node.last_lineno - @end_line <= 1 # if smaller or equal to one that means we are at the end of the method
26
+
27
+ @source_lines[@end_line..(@node.last_lineno - 2)]
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ProductionBreakpoints
4
+ # FIXME: this class is a mess, figure out interface and properly separate private / public
5
+ class Parser
6
+ attr_reader :root_node, :source_lines
7
+
8
+ def initialize(source_file)
9
+ @root_node = RubyVM::AbstractSyntaxTree.parse_file(source_file)
10
+ @source_lines = File.read(source_file).lines
11
+ @logger = ProductionBreakpoints.config.logger
12
+ end
13
+
14
+ # FIXME: set a max depth here to pretent unbounded recursion? probably should
15
+ def find_node(node, type, first, last, depth: 0)
16
+ child_nodes = node.children.select { |c| c.is_a?(RubyVM::AbstractSyntaxTree::Node) }
17
+ # @logger.debug("D: #{depth} #{node.type} has #{child_nodes.size} children and spans #{node.first_lineno}:#{node.first_column} to #{node.last_lineno}:#{node.last_column}")
18
+
19
+ if node.type == type && first >= node.first_lineno && last <= node.last_lineno
20
+ return node
21
+ end
22
+
23
+ child_nodes.map { |n| find_node(n, type, first, last, depth: depth + 1) }.flatten
24
+ end
25
+
26
+ def find_lineage(target)
27
+ lineage = _find_lineage(@root_node, target)
28
+ lineage.pop # FIXME: verify leafy node is equal to target or throw an error?
29
+ lineage
30
+ end
31
+
32
+ def find_definition_namespace(target)
33
+ lineage = find_lineage(target)
34
+
35
+ namespaces = []
36
+ lineage.each do |n|
37
+ next unless n.type == :MODULE || n.type == :CLASS
38
+
39
+ symbols = n.children.select { |c| c.is_a?(RubyVM::AbstractSyntaxTree::Node) && c.type == :COLON2 }
40
+ if symbols.size != 1
41
+ @logger.error("Couldn't determine symbol location for parent namespace")
42
+ end
43
+ symbol = symbols.first
44
+
45
+ symstr = @source_lines[symbol.first_lineno - 1][symbol.first_column..symbol.last_column].strip
46
+ namespaces << symstr
47
+ end
48
+
49
+ namespaces.join('::')
50
+ end
51
+
52
+ def find_definition_symbol(start_line, end_line)
53
+ def_node = _find_definition_node(@root_node, start_line, end_line)
54
+ def_column_start = def_node.first_column
55
+ def_column_end = _find_args_start(def_node).first_column
56
+ @source_lines[def_node.first_lineno - 1][(def_column_start + 3 + 1)..def_column_end].strip.to_sym
57
+ end
58
+
59
+ def find_definition_node(start_line, end_line)
60
+ _find_definition_node(@root_node, start_line, end_line)
61
+ end
62
+
63
+ # This method is a litle weird and pretty deep into metaprogramming, so i'll try to explain it
64
+ #
65
+ # Given the source method some_method, and a range of lines to apply the breakpoint to, we will inject
66
+ # calls two breakpoint methods. We will pass these calls the string representation of the original source code.
67
+ # If the string of original source is part of the "handle" block, it will run withing the binding
68
+ # of the method up to that point, and allow for us to run our custom handler method to apply our debugging automation.
69
+ #
70
+ # Any remaining code in the method also needs to be eval'd, as we want it to be recognized in the original binding,
71
+ # and the same binding as we've used for evaluating our handler. This allows us to keep local variables persisted
72
+ # "between blocks", as we want our breakpoint code to have no impact to the original bindings and source code.
73
+ #
74
+ # A generated breakpoint is shown below, the resulting string. is what will be evaluated on the method
75
+ # that we will prepend to the original parent in order to initiate our override.
76
+ #
77
+ # def some_method
78
+ # a = 1
79
+ # sleep 0.5
80
+ # b = a + 1
81
+ # ProductionBreakpoints.installed_breakpoints[:test_breakpoint_install].handle(Kernel.binding)
82
+ # end
83
+ #
84
+ def inject_metaprogramming_handlers(handler, def_start, def_end)
85
+ source = @source_lines.dup
86
+
87
+ source.insert(def_end - 1, "#{handler}\n") # FIXME: columns? and indenting?
88
+ source[(def_start - 1)..(def_end)].join
89
+ end
90
+
91
+ def ruby_source(start_line, end_line)
92
+ @source_lines[(start_line - 1)..(end_line - 1)].join
93
+ end
94
+
95
+ private
96
+
97
+ def _find_lineage(node, target, depth: 0)
98
+ child_nodes = node.children.select { |c| c.is_a?(RubyVM::AbstractSyntaxTree::Node) }
99
+ # @logger.debug("D: #{depth} #{node.type} has #{child_nodes.size} children and spans #{node.first_lineno}:#{node.first_column} to #{node.last_lineno}:#{node.last_column}")
100
+
101
+ if node.type == target.type &&
102
+
103
+ target.first_lineno >= node.first_lineno &&
104
+ target.last_lineno <= node.last_lineno
105
+ return [node]
106
+ end
107
+
108
+ parents = []
109
+ child_nodes.each do |n|
110
+ res = _find_lineage(n, target, depth: depth + 1)
111
+ unless res.empty?
112
+ res.unshift(n)
113
+ parents = res
114
+ end
115
+ end
116
+
117
+ parents.flatten
118
+ end
119
+
120
+ # FIXME: better error handling
121
+ def _find_definition_node(node, start_line, end_line)
122
+ defs = find_node(node, :DEFN, start_line, end_line)
123
+
124
+ if defs.size > 1
125
+ @logger.error('WHaaat? Multiple definitions found?! Bugs will probably follow')
126
+ end
127
+ defs.first
128
+ end
129
+
130
+ # FIXME: better error handling
131
+ def _find_args_start(def_node)
132
+ args = find_node(def_node, :ARGS, def_node.first_lineno, def_node.first_lineno)
133
+
134
+ if args.size > 1
135
+ @logger.error("I didn't think this was possible, I must have been wrong")
136
+ end
137
+ args.first
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ProductionBreakpoints
4
+ # Platform detection for ruby-static-tracing
5
+ module Platform
6
+ module_function
7
+
8
+ # Returns true if platform is linux
9
+ def linux?
10
+ /linux/.match(RUBY_PLATFORM)
11
+ end
12
+
13
+ # Returns true if platform is darwin
14
+ def darwin?
15
+ /darwin/.match(RUBY_PLATFORM)
16
+ end
17
+
18
+ # Returns true if platform is known to be supported
19
+ def supported_platform?
20
+ linux? || darwin?
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ProductionBreakpoints
4
+ # Validate that start line and end line point to code into the file
5
+ # At the momemt it will be valid if both start and end line points to valid ruby code
6
+ module StartEndLineValidator
7
+ module_function
8
+
9
+ def call(source_file, start_line, end_line)
10
+ source_lines = File.read(source_file).lines
11
+
12
+ !source_lines[start_line - 1].strip.empty? && !source_lines[end_line - 1].strip.empty?
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ProductionBreakpoints
4
+ # The current version of this gem
5
+ VERSION = '0.0.6'
6
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ require 'ruby-static-tracing'
6
+
7
+ require 'ruby-production-breakpoints/version'
8
+ require 'ruby-production-breakpoints/platform'
9
+ require 'ruby-production-breakpoints/errors'
10
+ require 'ruby-production-breakpoints/breakpoints'
11
+ require 'ruby-production-breakpoints/configuration'
12
+ require 'ruby-production-breakpoints/parser'
13
+ require 'ruby-production-breakpoints/method_override'
14
+ require 'ruby-production-breakpoints/start_end_line_validator'
15
+
16
+ module ProductionBreakpoints
17
+ extend self
18
+
19
+ attr_accessor :installed_breakpoints
20
+
21
+ self.installed_breakpoints = {} # FIXME: namespace by provider, to allow multiple BP per file
22
+
23
+ def configure
24
+ @configuration = Configuration.new
25
+ yield @configuration
26
+ @configuration.finish!
27
+ end
28
+
29
+ def config
30
+ unless defined?(@configuration)
31
+ raise NotConfiguredError
32
+ end
33
+
34
+ @configuration
35
+ end
36
+
37
+ # For now add new types here
38
+ def install_breakpoint(type, source_file, start_line, end_line, trace_id: 1)
39
+ # Hack to check if there is a supported breakpoint of this type for now
40
+ case type.name
41
+ when 'ProductionBreakpoints::Breakpoints::Latency'
42
+ when 'ProductionBreakpoints::Breakpoints::Inspect'
43
+ when 'ProductionBreakpoints::Breakpoints::Locals'
44
+ # logger.debug("Creating latency tracer")
45
+ # now rewrite source to call this created breakpoint through parser
46
+ else
47
+ config.logger.error("Unsupported breakpoint type #{type}")
48
+ end
49
+
50
+ breakpoint = type.new(source_file, start_line, end_line, trace_id: trace_id)
51
+ installed_breakpoints[trace_id.to_sym] = breakpoint
52
+ breakpoint.install
53
+ breakpoint.load
54
+ end
55
+
56
+ def disable_breakpoint(trace_id)
57
+ breakpoint = installed_breakpoints.delete(trace_id)
58
+ breakpoint.unload
59
+ breakpoint.uninstall
60
+ end
61
+
62
+ def disable!
63
+ installed_breakpoints.each do |trace_id, _bp|
64
+ disable_breakpoint(trace_id)
65
+ end
66
+ end
67
+
68
+ def sync!
69
+ # FIXME: don't just install, also remove - want to 'resync'
70
+ # logger.debug("Resync initiated")
71
+ desired = config.configured_breakpoints['breakpoints']
72
+
73
+ desired_trace_ids = desired.map { |bp| bp['trace_id'] }
74
+ installed_trace_ids = installed_breakpoints.keys
75
+
76
+ to_install_tids = desired_trace_ids - installed_trace_ids
77
+ to_remove_tids = installed_trace_ids - desired_trace_ids
78
+ to_install = desired.select { |bp| to_install_tids.include?(bp['trace_id']) }
79
+ # logger.debug("Will install #{to_install.size} breakpoints")
80
+ # logger.debug("Will remove #{to_remove_tids.size} breakpoints")
81
+ to_install.each do |bp|
82
+ handler = breakpoint_constant_for_type(bp)
83
+ unless valid_start_line_end_line?(bp['source_file'], bp['start_line'], bp['end_line'])
84
+ msg = <<~MSG
85
+ Skipping #{handler} for #{bp['source_file']}. start line and end line do not point to any code on the file.
86
+ MSG
87
+ config.logger.warn(msg)
88
+ next
89
+ end
90
+ install_breakpoint(handler, bp['source_file'], bp['start_line'], bp['end_line'], trace_id: bp['trace_id'])
91
+ end
92
+
93
+ to_remove_tids.each do |trace_id|
94
+ disable_breakpoint(trace_id)
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ def valid_start_line_end_line?(source_file, start_line, end_line)
101
+ StartEndLineValidator.call(source_file, start_line, end_line)
102
+ end
103
+
104
+ def breakpoint_constant_for_type(bp)
105
+ symstr = "ProductionBreakpoints::Breakpoints::#{bp['type'].capitalize}"
106
+ Object.const_get(symstr)
107
+ rescue NameError
108
+ config.logger.error("Could not find breakpoint handler for #{symstr}")
109
+ end
110
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-production-breakpoints
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dale Hamel
@@ -143,7 +143,21 @@ email: dale.hamel@srvthe.net
143
143
  executables: []
144
144
  extensions: []
145
145
  extra_rdoc_files: []
146
- files: []
146
+ files:
147
+ - lib/ruby-production-breakpoints.rb
148
+ - lib/ruby-production-breakpoints/breakpoints.rb
149
+ - lib/ruby-production-breakpoints/breakpoints/base.rb
150
+ - lib/ruby-production-breakpoints/breakpoints/inspect.rb
151
+ - lib/ruby-production-breakpoints/breakpoints/latency.rb
152
+ - lib/ruby-production-breakpoints/breakpoints/locals.rb
153
+ - lib/ruby-production-breakpoints/breakpoints/ustack.rb
154
+ - lib/ruby-production-breakpoints/configuration.rb
155
+ - lib/ruby-production-breakpoints/errors.rb
156
+ - lib/ruby-production-breakpoints/method_override.rb
157
+ - lib/ruby-production-breakpoints/parser.rb
158
+ - lib/ruby-production-breakpoints/platform.rb
159
+ - lib/ruby-production-breakpoints/start_end_line_validator.rb
160
+ - lib/ruby-production-breakpoints/version.rb
147
161
  homepage: https://github.com/dalehamel/ruby-production-breakpoints
148
162
  licenses:
149
163
  - MIT