chaos_detector 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|