chaos_detector 0.4.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/detect_chaos +31 -0
- data/lib/chaos_detector.rb +22 -0
- data/lib/chaos_detector/chaos_graphs/chaos_edge.rb +32 -0
- data/lib/chaos_detector/chaos_graphs/chaos_graph.rb +389 -0
- data/lib/chaos_detector/chaos_graphs/domain_metrics.rb +19 -0
- data/lib/chaos_detector/chaos_graphs/domain_node.rb +57 -0
- data/lib/chaos_detector/chaos_graphs/function_node.rb +112 -0
- data/lib/chaos_detector/chaos_graphs/module_node.rb +86 -0
- data/lib/chaos_detector/chaos_utils.rb +57 -0
- data/lib/chaos_detector/graph_theory/appraiser.rb +162 -0
- data/lib/chaos_detector/graph_theory/edge.rb +76 -0
- data/lib/chaos_detector/graph_theory/graph.rb +144 -0
- data/lib/chaos_detector/graph_theory/loop_detector.rb +32 -0
- data/lib/chaos_detector/graph_theory/node.rb +70 -0
- data/lib/chaos_detector/graph_theory/node_metrics.rb +68 -0
- data/lib/chaos_detector/graph_theory/reduction.rb +40 -0
- data/lib/chaos_detector/graphing/directed_graphs.rb +396 -0
- data/lib/chaos_detector/graphing/graphs.rb +129 -0
- data/lib/chaos_detector/graphing/matrix_graphs.rb +101 -0
- data/lib/chaos_detector/navigator.rb +237 -0
- data/lib/chaos_detector/options.rb +51 -0
- data/lib/chaos_detector/stacker/comp_info.rb +42 -0
- data/lib/chaos_detector/stacker/fn_info.rb +44 -0
- data/lib/chaos_detector/stacker/frame.rb +34 -0
- data/lib/chaos_detector/stacker/frame_stack.rb +63 -0
- data/lib/chaos_detector/stacker/mod_info.rb +24 -0
- data/lib/chaos_detector/tracker.rb +276 -0
- data/lib/chaos_detector/utils/core_util.rb +117 -0
- data/lib/chaos_detector/utils/fs_util.rb +49 -0
- data/lib/chaos_detector/utils/lerp_util.rb +20 -0
- data/lib/chaos_detector/utils/log_util.rb +45 -0
- data/lib/chaos_detector/utils/str_util.rb +90 -0
- data/lib/chaos_detector/utils/tensor_util.rb +21 -0
- data/lib/chaos_detector/walkman.rb +214 -0
- metadata +147 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'chaos_detector/chaos_utils'
|
2
|
+
require 'chaos_detector/graph_theory/node'
|
3
|
+
|
4
|
+
module ChaosDetector
|
5
|
+
class Options
|
6
|
+
extend ChaosDetector::Utils::CoreUtil::ChaosAttr
|
7
|
+
|
8
|
+
# TODO: Ability to run on self:
|
9
|
+
IGNORE_MODULES = %w[
|
10
|
+
ChaosDetector
|
11
|
+
ChaosUtils
|
12
|
+
RSpec
|
13
|
+
FactoryBot
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
IGNORE_PATHS = [
|
17
|
+
|
18
|
+
]
|
19
|
+
|
20
|
+
# chaos_attr (:options) { ChaosDetector::Options.new }
|
21
|
+
chaos_attr(:app_root_path, Dir.getwd)
|
22
|
+
chaos_attr(:log_root_path, 'logs')
|
23
|
+
chaos_attr(:graph_render_folder, 'render')
|
24
|
+
chaos_attr(:path_domain_hash)
|
25
|
+
chaos_attr(:ignore_modules, IGNORE_MODULES.dup)
|
26
|
+
chaos_attr(:ignore_paths, IGNORE_PATHS.dup)
|
27
|
+
chaos_attr(:module_filter, 'todo')
|
28
|
+
chaos_attr(:root_label, 'App Container')
|
29
|
+
chaos_attr(:frame_csv_path, 'csv/chaos_frames.csv')
|
30
|
+
chaos_attr(:walkman_buffer_length, 1000)
|
31
|
+
|
32
|
+
def path_with_root(key:nil, path:nil)
|
33
|
+
raise ArgumentError, "key: OR path: must be set" if key.nil? && path.nil?
|
34
|
+
|
35
|
+
subpath = key ? send(key.to_sym) : path.to_s
|
36
|
+
File.join(app_root_path, subpath)
|
37
|
+
end
|
38
|
+
|
39
|
+
def domain_from_path(local_path)
|
40
|
+
# dpath = Pathname.new(path.to_s).cleanpath.to_s
|
41
|
+
# @domain_hash[dpath] = group
|
42
|
+
#
|
43
|
+
|
44
|
+
# @domain_hash = {}
|
45
|
+
# @options.path_domain_hash && options.path_domain_hash.each do |path, group|
|
46
|
+
#
|
47
|
+
key = path_domain_hash.keys.find { |k| local_path.start_with?(k.to_s) }
|
48
|
+
key ? path_domain_hash[key] : ChaosDetector::GraphTheory::Node::ROOT_NODE_NAME
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'chaos_detector/chaos_utils'
|
2
|
+
|
3
|
+
module ChaosDetector
|
4
|
+
module Stacker
|
5
|
+
# Base class for Component (Module, FN) Infos
|
6
|
+
COMPONENT_TYPES = %i[function module domain].freeze
|
7
|
+
class CompInfo
|
8
|
+
attr_accessor :path
|
9
|
+
attr_accessor :name
|
10
|
+
attr_accessor :info
|
11
|
+
|
12
|
+
def initialize(name:, path: nil, info: nil)
|
13
|
+
@name = name
|
14
|
+
@path = path
|
15
|
+
@info = info
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
other &&
|
20
|
+
name == other.name &&
|
21
|
+
path == other.path &&
|
22
|
+
info == other.info
|
23
|
+
end
|
24
|
+
|
25
|
+
def eql?(other)
|
26
|
+
self == other
|
27
|
+
end
|
28
|
+
|
29
|
+
def hash
|
30
|
+
[path, name, info].hash
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
"#{name}: #{path} - #{info}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def component_type
|
38
|
+
raise NotImplementedError, 'Deriving class should implement #component_type'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'chaos_detector/chaos_utils'
|
2
|
+
require_relative 'comp_info'
|
3
|
+
module ChaosDetector
|
4
|
+
module Stacker
|
5
|
+
class FnInfo < ChaosDetector::Stacker::CompInfo
|
6
|
+
alias fn_name name
|
7
|
+
alias fn_line info
|
8
|
+
alias fn_path path
|
9
|
+
|
10
|
+
def initialize(fn_name:, fn_line: nil, fn_path: nil)
|
11
|
+
super(name: fn_name, path: fn_path, info: fn_line)
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
ChaosDetector::Stacker::FnInfo.match?(self, other)
|
16
|
+
end
|
17
|
+
|
18
|
+
def fn_info
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"##{fn_name}: #{fn_path}:L#{fn_line}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def component_type
|
27
|
+
:function
|
28
|
+
end
|
29
|
+
|
30
|
+
class << self
|
31
|
+
def match?(obj1, obj2, line_matching: false)
|
32
|
+
obj1.fn_path == obj2.fn_path && obj1.fn_name == obj2.fn_name
|
33
|
+
# (obj1.fn_name == obj2.fn_name || line_match?(obj1.fn_line, obj2.fn_line))
|
34
|
+
end
|
35
|
+
|
36
|
+
def line_match?(l1, l2)
|
37
|
+
return false if l1.nil? || l2.nil?
|
38
|
+
|
39
|
+
(l2 - l1).between?(0, 1)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'chaos_detector/chaos_utils'
|
2
|
+
require 'chaos_detector/stacker/mod_info'
|
3
|
+
require 'chaos_detector/stacker/fn_info'
|
4
|
+
|
5
|
+
# A single stack (tracepoint) frame
|
6
|
+
module ChaosDetector
|
7
|
+
module Stacker
|
8
|
+
class Frame
|
9
|
+
attr_reader :event # :call, :return, :superclass, :association, :class_association
|
10
|
+
attr_reader :mod_info
|
11
|
+
attr_reader :fn_info
|
12
|
+
attr_reader :caller_info
|
13
|
+
|
14
|
+
def initialize(event:, mod_info:, fn_info:, caller_info:)
|
15
|
+
raise ArgumentError, 'event is required' if ChaosUtils.naught?(event)
|
16
|
+
# raise ArgumentError, 'mod_info is required' if ChaosUtils.naught?(mod_info)
|
17
|
+
raise ArgumentError, 'fn_info is required' if ChaosUtils.naught?(fn_info)
|
18
|
+
|
19
|
+
@mod_info = mod_info
|
20
|
+
@fn_info = fn_info
|
21
|
+
@caller_info = caller_info
|
22
|
+
@event = event.to_sym
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
ChaosUtils.decorate_tuple(
|
27
|
+
[event, mod_info, fn_info, caller_info],
|
28
|
+
join_str: ' ',
|
29
|
+
clamp: :bracket
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative 'frame'
|
2
|
+
|
3
|
+
require 'chaos_detector/chaos_utils'
|
4
|
+
# Maintains all nodes and infers edges as stack calls are pushed and popped via Frames.
|
5
|
+
module ChaosDetector
|
6
|
+
module Stacker
|
7
|
+
class FrameStack
|
8
|
+
def initialize
|
9
|
+
@methods = []
|
10
|
+
@modules = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def log(msg, **opts)
|
14
|
+
ChaosUtils.log_msg(msg, subject: 'FrameStack', **opts)
|
15
|
+
end
|
16
|
+
|
17
|
+
def depth
|
18
|
+
@stack.length
|
19
|
+
end
|
20
|
+
|
21
|
+
def peek
|
22
|
+
@stack.first
|
23
|
+
end
|
24
|
+
|
25
|
+
def pop(frame)
|
26
|
+
raise ArgumentError, 'Current Frame is required' if frame.nil?
|
27
|
+
|
28
|
+
popped_frame, n_frame = @stack.each_with_index.find do |f, n|
|
29
|
+
if f == frame
|
30
|
+
true
|
31
|
+
elsif n.zero? && frame.fn_name == f.fn_name
|
32
|
+
# log("Matching #{f} \nto:\n #{frame} as most recent entry in stack.")
|
33
|
+
true
|
34
|
+
else
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# if n_frame.nil?
|
40
|
+
# log("Could not find #{frame} in stack")
|
41
|
+
# log(self.inspect)
|
42
|
+
# end
|
43
|
+
|
44
|
+
@stack.slice!(0..n_frame) unless n_frame.nil?
|
45
|
+
[popped_frame, n_frame]
|
46
|
+
end
|
47
|
+
|
48
|
+
def push(frame)
|
49
|
+
@stack.unshift(frame)
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
'Frames: %d' % depth
|
54
|
+
end
|
55
|
+
|
56
|
+
def inspect
|
57
|
+
msg = "#{self}\n"
|
58
|
+
msg << ChaosUtils.decorate_tuple(@stack.map { |f| f.to_s}, join_str: " -> \n", indent_length: 2, clamp: :none)
|
59
|
+
msg
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'chaos_detector/utils/str_util'
|
2
|
+
require 'chaos_detector/chaos_utils'
|
3
|
+
require_relative 'comp_info'
|
4
|
+
module ChaosDetector
|
5
|
+
module Stacker
|
6
|
+
class ModInfo < ChaosDetector::Stacker::CompInfo
|
7
|
+
alias mod_name name
|
8
|
+
alias mod_type info
|
9
|
+
alias mod_path path
|
10
|
+
|
11
|
+
def initialize(mod_name:, mod_type: nil, mod_path: nil)
|
12
|
+
super(name: mod_name, path: mod_path, info: mod_type)
|
13
|
+
end
|
14
|
+
|
15
|
+
def component_type
|
16
|
+
:module
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
format('(%s) %s - %s', mod_type, ChaosDetector::Utils::StrUtil.humanize_module(mod_name, sep_token: '::'), ChaosDetector::Utils::StrUtil.humanize_module(mod_path, sep_token: '/'))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,276 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'pathname'
|
3
|
+
require_relative 'options'
|
4
|
+
require_relative 'stacker/mod_info'
|
5
|
+
require_relative 'chaos_graphs/module_node'
|
6
|
+
require_relative 'stacker/frame'
|
7
|
+
require_relative 'walkman'
|
8
|
+
require 'chaos_detector/chaos_utils'
|
9
|
+
|
10
|
+
# The main interface for intercepting tracepoints,
|
11
|
+
# and converting them into recordable and playable
|
12
|
+
# stack/trace frames
|
13
|
+
|
14
|
+
module ChaosDetector
|
15
|
+
class Tracker
|
16
|
+
REGEX_MODULE_UNDECORATE = /#<(Class:)?([a-zA-Z\:]*)(.*)>/.freeze
|
17
|
+
TRACE_METHOD_EVENTS = %i[call return].freeze
|
18
|
+
|
19
|
+
attr_reader :options
|
20
|
+
attr_reader :walkman
|
21
|
+
|
22
|
+
def initialize(options:)
|
23
|
+
raise ArgumentError, '#initialize requires options' if options.nil?
|
24
|
+
|
25
|
+
@options = options
|
26
|
+
@total_frames = 0
|
27
|
+
@total_traces = 0
|
28
|
+
apply_options
|
29
|
+
end
|
30
|
+
|
31
|
+
def record
|
32
|
+
log("Detecting chaos at #{@app_root_path}")
|
33
|
+
# log(caller_locations.join("\n\t->\t"))
|
34
|
+
# log("")
|
35
|
+
@stopped = false
|
36
|
+
@walkman.record_start
|
37
|
+
@total_traces = 0
|
38
|
+
@trace = TracePoint.new(*TRACE_METHOD_EVENTS) do |tracepoint|
|
39
|
+
if @stopped
|
40
|
+
@trace.disable
|
41
|
+
log('Tracing stopped; stopping immediately.')
|
42
|
+
next
|
43
|
+
end
|
44
|
+
|
45
|
+
tp_path = tracepoint.path
|
46
|
+
next if full_path_skip?(tp_path)
|
47
|
+
|
48
|
+
tp_class = tracepoint.defined_class
|
49
|
+
|
50
|
+
# trace_mod_details(tracepoint)
|
51
|
+
mod_info = mod_info_at(tp_class, mod_full_path: tp_path)
|
52
|
+
# puts "mod_info: #{mod_info} #{tp_class.respond_to?(:superclass) && tp_class.superclass}"
|
53
|
+
next unless mod_info
|
54
|
+
|
55
|
+
fn_info = fn_info_at(tracepoint)
|
56
|
+
e = tracepoint.event
|
57
|
+
@trace.disable do
|
58
|
+
@total_traces += 1
|
59
|
+
caller_info = extract_caller(tracepoint, fn_info)
|
60
|
+
write_event_frame(e, fn_info: fn_info, mod_info: mod_info, caller_info: caller_info)
|
61
|
+
|
62
|
+
# Detect superclass association:
|
63
|
+
ChaosUtils.with(superclass_mod_info(tp_class)) do |super_mod_info|
|
64
|
+
# puts "Would superclass #{mod_info} with #{super_mod_info}"
|
65
|
+
write_event_frame(:superclass, fn_info: fn_info, mod_info: mod_info, caller_info: super_mod_info)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Detect associations:
|
69
|
+
# puts "UGGGGG: #{tp_class.singleton_class.included_modules}"
|
70
|
+
ancestor_mod_infos(tp_class, tp_class.included_modules).each do |agg_mod_info|
|
71
|
+
# puts "Would ancestors with #{agg_mod_info}"
|
72
|
+
write_event_frame(:association, fn_info: fn_info, mod_info: mod_info, caller_info: agg_mod_info)
|
73
|
+
end
|
74
|
+
|
75
|
+
# DerivedFracker.singleton_class.included_modules # MixinCD, Kernel
|
76
|
+
ancestor_mod_infos(tp_class, tp_class.singleton_class.included_modules).each do |agg_mod_info|
|
77
|
+
# puts "WOULD CLASS ancestors with #{agg_mod_info}"
|
78
|
+
write_event_frame(:class_association, fn_info: fn_info, mod_info: mod_info, caller_info: agg_mod_info)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Detect class associations:
|
82
|
+
# ancestor_mod_infos(tp_class).each do |agg_mod_info|
|
83
|
+
# puts "Would ancestors with #{agg_mod_info}"
|
84
|
+
# write_event_frame(:association, fn_info: fn_info, mod_info: mod_info, caller_info: super_mod_info)
|
85
|
+
# end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
@trace.enable
|
89
|
+
true
|
90
|
+
end
|
91
|
+
|
92
|
+
def write_event_frame(event, fn_info:, mod_info:, caller_info:)
|
93
|
+
ChaosDetector::Stacker::Frame.new(
|
94
|
+
event: event,
|
95
|
+
mod_info: mod_info,
|
96
|
+
fn_info: fn_info,
|
97
|
+
caller_info: caller_info
|
98
|
+
).tap do |frame|
|
99
|
+
@walkman.write_frame(frame)
|
100
|
+
@total_frames += 1
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def stop
|
105
|
+
@stopped = true
|
106
|
+
@trace&.disable
|
107
|
+
log("Stopping after total traces: #{@total_traces}")
|
108
|
+
@walkman.stop
|
109
|
+
end
|
110
|
+
|
111
|
+
# Undecorate all this junk:
|
112
|
+
# a="#<Class:Authentication>"
|
113
|
+
# b="#<Class:Person(id: integer, first"
|
114
|
+
# c="#<ChaosDetector::Node:0x00007fdd5d2c6b08>"
|
115
|
+
|
116
|
+
# Blank class get mod_class for tracepoint. [#<Class:#<Parslet::Context:0x00007fa90ee06c80>>]
|
117
|
+
# MMMM >>> (word), (default), (word), (lib/email_parser.rb):L106, (#<Parslet::Context:0x00007fa90ee06c80>)
|
118
|
+
def undecorate_module_name(mod_name)
|
119
|
+
return nil if ChaosUtils.naught?(mod_name)
|
120
|
+
return mod_name unless mod_name.start_with?('#')
|
121
|
+
|
122
|
+
plain_name = nil
|
123
|
+
caps = mod_name.match(REGEX_MODULE_UNDECORATE)&.captures
|
124
|
+
# puts "CAP #{mod_name}: #{caps}"
|
125
|
+
if caps && caps.length > 0
|
126
|
+
caps.delete('Class:')
|
127
|
+
caps.compact!
|
128
|
+
plain_name = caps.first
|
129
|
+
plain_name&.chomp!(':')
|
130
|
+
end
|
131
|
+
|
132
|
+
plain_name || mod_name
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def apply_options
|
138
|
+
@walkman = ChaosDetector::Walkman.new(options: @options)
|
139
|
+
@app_root_path = ChaosUtils.with(@options.app_root_path) { |p| Pathname.new(p)&.to_s}
|
140
|
+
end
|
141
|
+
|
142
|
+
def extract_caller(tracepoint, fn_info)
|
143
|
+
callers = tracepoint.self.send(:caller_locations)
|
144
|
+
callers = callers.select do |bt|
|
145
|
+
!full_path_skip?(bt.absolute_path) &&
|
146
|
+
ChaosUtils.aught?(bt.base_label) &&
|
147
|
+
!bt.base_label.start_with?('<')
|
148
|
+
end
|
149
|
+
|
150
|
+
frame_at = callers.index { |bt| bt.base_label == fn_info.fn_name && localize_path(bt.absolute_path) == fn_info.fn_path }
|
151
|
+
bt_caller = frame_at.nil? ? nil : callers[frame_at + 1]
|
152
|
+
ChaosUtils.with(bt_caller) do |bt|
|
153
|
+
ChaosDetector::Stacker::FnInfo.new(
|
154
|
+
fn_name: bt.base_label,
|
155
|
+
fn_line: bt.lineno,
|
156
|
+
fn_path: localize_path(bt.absolute_path)
|
157
|
+
)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def superclass_mod_info(clz)
|
162
|
+
return nil unless clz&.respond_to?(:superclass)
|
163
|
+
|
164
|
+
sup_clz = clz.superclass
|
165
|
+
|
166
|
+
# puts "BOOOO::: #{clz.superclass} <> #{sup_clz} ~> ChaosUtils.aught?(sup_clz)"
|
167
|
+
return nil unless ChaosUtils.aught?(sup_clz)
|
168
|
+
|
169
|
+
# puts "DDDDDDDDDDD::: #{sup_clz&.name}"
|
170
|
+
|
171
|
+
mod_info_at(sup_clz)
|
172
|
+
end
|
173
|
+
|
174
|
+
def ancestor_mod_infos(clz, clz_modules)
|
175
|
+
sup_clz = clz.superclass rescue nil
|
176
|
+
|
177
|
+
ancestors = clz_modules.filter_map do |c|
|
178
|
+
if c != clz && (sup_clz.nil? || c != sup_clz)
|
179
|
+
mod_info_at(c)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
ancestors.compact
|
184
|
+
end
|
185
|
+
|
186
|
+
def mod_info_at(mod_class, mod_full_path: nil)
|
187
|
+
return nil unless mod_class
|
188
|
+
|
189
|
+
mod_name = mod_name_from_class(mod_class)
|
190
|
+
if ChaosUtils.aught?(mod_name)
|
191
|
+
mod_type = mod_type_from_class(mod_class)
|
192
|
+
mod_fp = ChaosUtils.aught?(mod_full_path) ? mod_full_path : nil
|
193
|
+
mod_fp ||= mod_class.const_source_location(mod_name)&.first
|
194
|
+
safe_mod_info(mod_name, mod_type, mod_fp)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def fn_info_at(tracepoint)
|
199
|
+
ChaosDetector::Stacker::FnInfo.new(fn_name: tracepoint.callee_id.to_s, fn_line: tracepoint.lineno, fn_path: localize_path(tracepoint.path))
|
200
|
+
end
|
201
|
+
|
202
|
+
# TODO: MAKE more LIKE module_skip below:
|
203
|
+
def full_path_skip?(path)
|
204
|
+
return true unless ChaosUtils.aught?(path)
|
205
|
+
|
206
|
+
if !(@app_root_path && path.start_with?(@app_root_path))
|
207
|
+
true
|
208
|
+
# elsif path.start_with?('/Users/stevenmiers/src/sci-ex/sciex3/lib/mixins')
|
209
|
+
# false
|
210
|
+
else
|
211
|
+
rel_path = localize_path(path)
|
212
|
+
@options.ignore_paths.any? { |p| rel_path.start_with?(p)}
|
213
|
+
# false
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def module_skip?(mod_name)
|
218
|
+
ChaosUtils.with(mod_name) do |mod|
|
219
|
+
@options.ignore_modules.any? { |m| mod.start_with?(m)}
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def mod_type_from_class(clz)
|
224
|
+
case clz
|
225
|
+
when Class
|
226
|
+
:class
|
227
|
+
when Module
|
228
|
+
:module
|
229
|
+
else
|
230
|
+
log "Unknown mod_type: #{clz}"
|
231
|
+
:nil
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def mod_name_from_class(clz)
|
236
|
+
mod_name = clz.name
|
237
|
+
mod_name = clz.to_s unless check_name(mod_name)
|
238
|
+
|
239
|
+
undecorate_module_name(mod_name)
|
240
|
+
end
|
241
|
+
|
242
|
+
def localize_path(path)
|
243
|
+
# @app_root_path.relative_path_from(Pathname.new(path).cleanpath).to_s
|
244
|
+
return '' unless ChaosUtils.aught?(path)
|
245
|
+
|
246
|
+
p = Pathname.new(path).cleanpath.to_s
|
247
|
+
p.sub!(@app_root_path, '') if @app_root_path
|
248
|
+
local_path = p.start_with?('/') ? p[1..-1] : p
|
249
|
+
local_path.to_s
|
250
|
+
end
|
251
|
+
|
252
|
+
def log(msg, **opts)
|
253
|
+
ChaosUtils.log_msg(msg, subject: 'Tracker', **opts)
|
254
|
+
end
|
255
|
+
|
256
|
+
def trace_mod_details(tp, label: 'ModDetails')
|
257
|
+
log format('Tracepoint [%s] (%s): %s / %s [%s / %s]', label, tp.event, tp.defined_class, tp.self.class, tp.defined_class&.name, tp.self.class&.name)
|
258
|
+
end
|
259
|
+
|
260
|
+
def check_name(mod_nm)
|
261
|
+
ChaosUtils.aught?(mod_nm) && !mod_nm.strip.start_with?('#')
|
262
|
+
end
|
263
|
+
|
264
|
+
def safe_mod_info(mod_name, mod_type, mod_full_path)
|
265
|
+
return nil if full_path_skip?(mod_full_path)
|
266
|
+
return nil if module_skip?(mod_name)
|
267
|
+
# puts ['mod_full_path', mod_full_path].inspect
|
268
|
+
|
269
|
+
ChaosDetector::Stacker::ModInfo.new(
|
270
|
+
mod_name: mod_name,
|
271
|
+
mod_path: localize_path(mod_full_path),
|
272
|
+
mod_type: mod_type
|
273
|
+
)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|