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,129 @@
|
|
1
|
+
require 'chaos_detector/options'
|
2
|
+
require 'chaos_detector/navigator'
|
3
|
+
require 'chaos_detector/stacker/frame'
|
4
|
+
require 'chaos_detector/graph_theory/appraiser'
|
5
|
+
require 'chaos_detector/graph_theory/graph'
|
6
|
+
require 'chaos_detector/graphing/directed_graphs'
|
7
|
+
require 'chaos_detector/chaos_graphs/chaos_graph'
|
8
|
+
require 'chaos_detector/utils/str_util'
|
9
|
+
require 'chaos_detector/chaos_utils'
|
10
|
+
|
11
|
+
require_relative 'directed_graphs'
|
12
|
+
require_relative 'matrix_graphs'
|
13
|
+
|
14
|
+
module ChaosDetector
|
15
|
+
module Graphing
|
16
|
+
### Top-level Chaos-detection graphing and rendering
|
17
|
+
class Graphs
|
18
|
+
attr_reader :navigator
|
19
|
+
attr_reader :chaos_graph
|
20
|
+
|
21
|
+
def initialize(options: nil)
|
22
|
+
@options = options || ChaosDetector::Options.new
|
23
|
+
@render_folder = @options.path_with_root(key: :graph_render_folder)
|
24
|
+
# @render_folder = @options.graph_render_folder
|
25
|
+
ChaosDetector::Utils::FSUtil.ensure_dirpath(@render_folder)
|
26
|
+
@navigator = ChaosDetector::Navigator.new(options: @options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def playback(row_range: nil)
|
30
|
+
fn_graph, mod_graph = @navigator.playback(row_range: row_range)
|
31
|
+
@chaos_graph = ChaosDetector::ChaosGraphs::ChaosGraph.new(fn_graph, mod_graph)
|
32
|
+
@chaos_graph.infer_all
|
33
|
+
end
|
34
|
+
|
35
|
+
GRAPH_TYPE_ATTRS = {
|
36
|
+
domain: {
|
37
|
+
# ratio: 'auto',
|
38
|
+
# size: '8, 8',
|
39
|
+
# rankdir: 'TB',
|
40
|
+
# packmode: 'clust'
|
41
|
+
},
|
42
|
+
function: {
|
43
|
+
|
44
|
+
},
|
45
|
+
module: {
|
46
|
+
rankdir: 'TB',
|
47
|
+
packmode: 'clust',
|
48
|
+
ranksep: '1.5',
|
49
|
+
},
|
50
|
+
}
|
51
|
+
|
52
|
+
def adjacency_graph(graph_type, graph: nil, graph_name: 'adj-matrix')
|
53
|
+
the_matrix = ChaosDetector::Graphing::MatrixGraphs.new(chaos_graph, render_folder: @render_folder)
|
54
|
+
graph, appraiser = @chaos_graph.graph_data_for(graph_type: graph_type)
|
55
|
+
matrix = appraiser.adjacency_matrix
|
56
|
+
the_matrix.render_adjacency(matrix, graph_name: graph_name)
|
57
|
+
end
|
58
|
+
|
59
|
+
def render_dep_graph(graph_type, graph: nil, as_cluster: false, domains: false, name: nil, root: true, metrics_table: false)
|
60
|
+
g, _appraiser = chaos_graph.graph_data_for(graph_type: graph_type)
|
61
|
+
rgraph = graph ? graph : g
|
62
|
+
graph_name = name ? name : "#{graph_type}-dep"
|
63
|
+
|
64
|
+
graph_attrs = GRAPH_TYPE_ATTRS[graph_type]
|
65
|
+
|
66
|
+
dgraph = if domains #&& graph_type != :cluster
|
67
|
+
build_domain_dgraph(graph_name, rgraph.nodes, rgraph.edges, graph_attrs: graph_attrs, metrics_table: metrics_table)
|
68
|
+
else
|
69
|
+
build_dgraph(graph_name, rgraph.nodes, rgraph.edges, as_cluster: as_cluster, graph_attrs: graph_attrs, metrics_table: metrics_table)
|
70
|
+
end
|
71
|
+
|
72
|
+
dgraph.rendered_path
|
73
|
+
end
|
74
|
+
|
75
|
+
def render_domain_dep(graph_name: 'domain-dep', domain_graph: nil, metrics_table: false)
|
76
|
+
render_dep_graph(:domain, as_cluster: true, graph: domain_graph, name: graph_name, metrics_table: metrics_table)
|
77
|
+
end
|
78
|
+
|
79
|
+
def render_fn_dep(graph_name: 'fn-dep', function_graph: nil, domains: false, metrics_table: false)
|
80
|
+
render_dep_graph(:function, as_cluster: true, graph: function_graph, domains: domains, name: graph_name, metrics_table: metrics_table)
|
81
|
+
end
|
82
|
+
|
83
|
+
def render_mod_dep(graph_name: 'module-dep', module_graph: nil, domains: false, metrics_table: false)
|
84
|
+
render_dep_graph(:module, graph: module_graph, domains: domains, name: graph_name, metrics_table: metrics_table)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def build_dgraph(label, nodes, edges, as_cluster: false, render: true, graph_attrs: nil, metrics_table: false)
|
90
|
+
# nodes.each do |n|
|
91
|
+
# p("#{label} Nodes: #{ChaosUtils.decorate(n.title)}")
|
92
|
+
# end
|
93
|
+
|
94
|
+
# edges.each do |e|
|
95
|
+
# p("#{label} Edges: #{ChaosUtils.decorate(e.src_node.title)} -> #{ChaosUtils.decorate(e.dep_node.title)}")
|
96
|
+
# end
|
97
|
+
puts("Building #{label} with as_cluster: #{as_cluster}")
|
98
|
+
dgraph = ChaosDetector::Graphing::DirectedGraphs.new(render_folder: @render_folder)
|
99
|
+
dgraph.create_directed_graph(label, graph_attrs: graph_attrs)
|
100
|
+
dgraph.append_nodes(nodes, as_cluster: as_cluster, metrics_table: metrics_table)
|
101
|
+
dgraph.add_edges(edges)
|
102
|
+
dgraph.render_graph if render
|
103
|
+
dgraph
|
104
|
+
end
|
105
|
+
|
106
|
+
def build_domain_dgraph(graph_name, nodes, edges, render: true, graph_attrs: nil, metrics_table: false)
|
107
|
+
# Add domains as cluster/subgraph nodes:
|
108
|
+
domain_nodes = nodes.map{|node| chaos_graph.domain_node_for(name: node.domain_name) }
|
109
|
+
dgraph = build_dgraph(graph_name, domain_nodes, [], as_cluster: true, render: false, graph_attrs: graph_attrs, metrics_table: metrics_table)
|
110
|
+
|
111
|
+
# Add nodes to domains:
|
112
|
+
dgraph.append_nodes(nodes, metrics_table: false) do |node|
|
113
|
+
chaos_graph.domain_node_for(name: node.domain_name)
|
114
|
+
end
|
115
|
+
dgraph.add_edges(edges)
|
116
|
+
dgraph.render_graph if render
|
117
|
+
dgraph
|
118
|
+
# dgraph.rendered_path
|
119
|
+
|
120
|
+
# fn_nodes = chaos_graph.function_graph.nodes.group_by(&:domain_name)
|
121
|
+
# fn_nodes.map do |dom_nm, fn_nodes|
|
122
|
+
# dom_node = ChaosUtils.aught?(dom_nm) && chaos_graph.domain_node_for(name: dom_nm)
|
123
|
+
# dgraph.add_node_to_parent(fn_node, dom_node)
|
124
|
+
# end
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'ruby-graphviz'
|
3
|
+
require 'rubyvis'
|
4
|
+
|
5
|
+
require 'chaos_detector/utils/str_util'
|
6
|
+
require 'chaos_detector/chaos_utils'
|
7
|
+
|
8
|
+
module ChaosDetector
|
9
|
+
module Graphing
|
10
|
+
class MatrixGraphs
|
11
|
+
CELL_WIDTH = 100
|
12
|
+
NodeStruct = Struct.new(:node_name, :node_value, :group, :index, :link_degree)
|
13
|
+
LinkNodeStruct = Struct.new(:index, :link_degree)
|
14
|
+
LinkStruct = Struct.new(:source, :target, :source_node, :target_node, :value, :link_value, :link_degree)
|
15
|
+
|
16
|
+
def initialize(chaos_graph, render_folder: nil)
|
17
|
+
@chaos_graph = chaos_graph
|
18
|
+
@render_folder = render_folder || 'render'
|
19
|
+
end
|
20
|
+
|
21
|
+
def simple_graph_struct(graph)
|
22
|
+
groupset = graph.nodes.reduce(Set.new()) { |memo, n| memo.add(n.domain_name) }
|
23
|
+
groups = groupset.to_a.sort!
|
24
|
+
|
25
|
+
nodes = graph.nodes.map.with_index do |node, n|
|
26
|
+
group_index = groups.index(node.domain_name)
|
27
|
+
NodeStruct.new(node.title, node.to_k, group_index, n)
|
28
|
+
end
|
29
|
+
|
30
|
+
links = graph.edges.map do |edge|
|
31
|
+
w = edge.weight
|
32
|
+
src = nodes.index(edge.src_node)
|
33
|
+
dep = nodes.index(edge.dep_node)
|
34
|
+
LinkStruct.new(src, dep, LinkNodeStruct.new(0, w), LinkNodeStruct.new(0, w), w, w )
|
35
|
+
end
|
36
|
+
|
37
|
+
[nodes, links]
|
38
|
+
end
|
39
|
+
|
40
|
+
def render_adjacency(matrix, graph_name: 'adj-matrix')
|
41
|
+
|
42
|
+
puts "MATRIX COUNT: #{matrix.row_size} x #{matrix.column_size} = #{matrix.count}"
|
43
|
+
matrix.row_vectors.each{|v| puts v.to_a.inspect}
|
44
|
+
|
45
|
+
# simple_nodes, simple_links = simple_graph_struct(graph)
|
46
|
+
|
47
|
+
w = Math.sqrt(matrix.count) * CELL_WIDTH
|
48
|
+
h = w
|
49
|
+
|
50
|
+
color=Rubyvis::Colors.category19
|
51
|
+
|
52
|
+
vis = Rubyvis::Panel.new() do
|
53
|
+
width w
|
54
|
+
height h
|
55
|
+
top 90
|
56
|
+
left 90
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
# layout_matrix do
|
61
|
+
# nodes simple_nodes
|
62
|
+
# links simple_links
|
63
|
+
# # sort {|a,b| b.group<=>a.group }
|
64
|
+
# directed (true)
|
65
|
+
# link.bar do
|
66
|
+
# fill_style {|l| l.link_value!=0 ?
|
67
|
+
# ((l.target_node.group == l.source_node.group) ? color[l.source_node.group] : "#555") : "#eee"}
|
68
|
+
# antialias(false)
|
69
|
+
# line_width(1)
|
70
|
+
# end
|
71
|
+
# node_label.label do
|
72
|
+
# text_style {|l| color[l.group]}
|
73
|
+
# end
|
74
|
+
# end
|
75
|
+
|
76
|
+
# vis
|
77
|
+
# .add(Rubyvis::Layout::Grid)
|
78
|
+
# .rows(matrix.to_a)
|
79
|
+
# .cell
|
80
|
+
# .add(Rubyvis::Bar)
|
81
|
+
# .fill_style(Rubyvis.ramp("white", "black"))
|
82
|
+
# .anchor("center").
|
83
|
+
# .add(Rubyvis::Label)
|
84
|
+
# .text_style(Rubyvis.ramp("black","white"))
|
85
|
+
# .text(lambda{|v| v.is_a?(Numeric) ? ("%0.2f" % v) : v.to_s})
|
86
|
+
|
87
|
+
vis.render();
|
88
|
+
svg = vis.to_svg()
|
89
|
+
|
90
|
+
@rendered_path = File.join(@render_folder, "#{graph_name}.svg").to_s
|
91
|
+
ChaosDetector::Utils::FSUtil.safe_file_write(@rendered_path, content: svg)
|
92
|
+
@rendered_path
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def log(msg, **opts)
|
97
|
+
ChaosUtils.log_msg(msg, subject: 'MatrixDiagram', **opts)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require_relative 'options'
|
3
|
+
require_relative 'stacker/mod_info'
|
4
|
+
require_relative 'graph_theory/graph'
|
5
|
+
require_relative 'chaos_graphs/function_node'
|
6
|
+
require_relative 'chaos_graphs/module_node'
|
7
|
+
require_relative 'stacker/frame'
|
8
|
+
require_relative 'walkman'
|
9
|
+
require 'chaos_detector/chaos_utils'
|
10
|
+
|
11
|
+
# The main interface for intercepting tracepoints,
|
12
|
+
# and converting them into recordable and playable
|
13
|
+
# stack/trace frames
|
14
|
+
|
15
|
+
module ChaosDetector
|
16
|
+
class Navigator
|
17
|
+
REGEX_MODULE_UNDECORATE = /#<(Class:)?([a-zA-Z\:]*)(.*)>/.freeze
|
18
|
+
DEFAULT_GROUP = 'default'.freeze
|
19
|
+
FRAME_ACTIONS = [:return].freeze # , :return, :class, :end]
|
20
|
+
|
21
|
+
attr_reader :options
|
22
|
+
attr_reader :domain_hash
|
23
|
+
attr_reader :walkman
|
24
|
+
|
25
|
+
attr_reader :nodes
|
26
|
+
attr_reader :edges
|
27
|
+
|
28
|
+
attr_reader :mod_nodes
|
29
|
+
attr_reader :mod_edges
|
30
|
+
|
31
|
+
def initialize(options:)
|
32
|
+
raise ArgumentError, '#initialize requires options' if options.nil?
|
33
|
+
|
34
|
+
@options = options
|
35
|
+
apply_options
|
36
|
+
end
|
37
|
+
|
38
|
+
### Playback of walkman CSV file:
|
39
|
+
def playback(row_range: nil)
|
40
|
+
log('Chaos playing through navigator. Total lines: ', object: @walkman.count)
|
41
|
+
|
42
|
+
@nodes = Set.new
|
43
|
+
@edges_call = Set.new
|
44
|
+
@edges_ret = Set.new
|
45
|
+
@mod_nodes = Set.new
|
46
|
+
@mod_edges = Set.new
|
47
|
+
@err_nodes = Set.new
|
48
|
+
|
49
|
+
@walkman.playback(row_range: row_range) do |_rownum, frame|
|
50
|
+
perform_node_action(frame)
|
51
|
+
end
|
52
|
+
# log('Found nodes.', object: @nodes.length)
|
53
|
+
|
54
|
+
@walkman.playback(row_range: row_range) do |_rownum, frame|
|
55
|
+
if [:call, :return].include?(frame.event)
|
56
|
+
perform_edge_action(frame)
|
57
|
+
else
|
58
|
+
perform_mod_edge_action(frame)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
edges = merge_edges.to_a
|
63
|
+
# log('Found edges.', object: edges.length)
|
64
|
+
|
65
|
+
@mod_graph = ChaosDetector::GraphTheory::Graph.new(
|
66
|
+
root_node: ChaosDetector::ChaosGraphs::ModuleNode.root_node(force_new: true),
|
67
|
+
nodes: @mod_nodes.to_a,
|
68
|
+
edges: @mod_edges.to_a
|
69
|
+
)
|
70
|
+
|
71
|
+
@fn_graph = ChaosDetector::GraphTheory::Graph.new(
|
72
|
+
root_node: ChaosDetector::ChaosGraphs::FunctionNode.root_node(force_new: true),
|
73
|
+
nodes: @nodes.to_a,
|
74
|
+
edges: edges
|
75
|
+
)
|
76
|
+
|
77
|
+
[@fn_graph, @mod_graph]
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def apply_options
|
83
|
+
@walkman = ChaosDetector::Walkman.new(options: @options)
|
84
|
+
@domain_hash = {}
|
85
|
+
# @options.path_domain_hash && options.path_domain_hash.each do |path, group|
|
86
|
+
# dpath = Pathname.new(path.to_s).cleanpath.to_s
|
87
|
+
# @domain_hash[dpath] = group
|
88
|
+
# end
|
89
|
+
end
|
90
|
+
|
91
|
+
# We merge/reduce edges elsewhere:
|
92
|
+
def merge_edges
|
93
|
+
c = Set.new(@edges_call)
|
94
|
+
r = Set.new(@edges_ret)
|
95
|
+
|
96
|
+
raise 'Call Edges should be Set' unless c.length == @edges_call.length
|
97
|
+
raise 'Ret Edges should be Set' unless r.length == @edges_ret.length
|
98
|
+
|
99
|
+
raise 'Call Edges should be unique' unless @edges_call.uniq.length == @edges_call.length
|
100
|
+
raise 'Call Edges should be unique' unless @edges_ret.uniq.length == @edges_ret.length
|
101
|
+
|
102
|
+
# log('Unique edges in call (n/total)', object: [(c - r).length, c.length])
|
103
|
+
# log('Unique edges in return (n/total)', object: [(r - c).length, r.length])
|
104
|
+
|
105
|
+
# @edges_call.each do |e|
|
106
|
+
# log("edges_call", object: e)
|
107
|
+
# end
|
108
|
+
|
109
|
+
# @edges_ret.each do |e|
|
110
|
+
# log("edges_ret ", object: e)
|
111
|
+
# end
|
112
|
+
|
113
|
+
c.union(r)
|
114
|
+
end
|
115
|
+
|
116
|
+
def fn_node_for(fn_info)
|
117
|
+
return nil unless fn_info&.fn_name
|
118
|
+
|
119
|
+
@nodes.find do |n|
|
120
|
+
n.fn_name == fn_info.fn_name &&
|
121
|
+
n.fn_path == fn_info.fn_path
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def mod_node_for(mod_info)
|
126
|
+
return nil unless mod_info&.mod_name
|
127
|
+
|
128
|
+
@mod_nodes.find do |n|
|
129
|
+
n.mod_name == mod_info.mod_name &&
|
130
|
+
n.mod_path == mod_info.mod_path
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# @return Node matching given frame or create a new one.
|
135
|
+
def fn_node_for_frame(frame)
|
136
|
+
# log("Calling fn_node_for_frame", object: frame)
|
137
|
+
node = fn_node_for(frame.fn_info)
|
138
|
+
|
139
|
+
if node.nil? && frame.event == :call
|
140
|
+
fn_info = frame.fn_info
|
141
|
+
node = ChaosDetector::ChaosGraphs::FunctionNode.new(
|
142
|
+
fn_name: fn_info.fn_name,
|
143
|
+
fn_path: fn_info.fn_path,
|
144
|
+
fn_line: fn_info.fn_line,
|
145
|
+
domain_name: options.domain_from_path(fn_info.fn_path),
|
146
|
+
mod_info: frame.mod_info
|
147
|
+
)
|
148
|
+
@nodes << node
|
149
|
+
end
|
150
|
+
|
151
|
+
node
|
152
|
+
end
|
153
|
+
|
154
|
+
def mod_node_from_info(mod_info)
|
155
|
+
# log("Calling fn_node_for_frame", object: frame)
|
156
|
+
node = mod_node_for(mod_info)
|
157
|
+
|
158
|
+
if node.nil? #&& frame.event == :call
|
159
|
+
node = ChaosDetector::ChaosGraphs::ModuleNode.new(
|
160
|
+
mod_name: mod_info.mod_name,
|
161
|
+
mod_path: mod_info.mod_path,
|
162
|
+
mod_type: mod_info.mod_type,
|
163
|
+
domain_name: options.domain_from_path(mod_info.mod_path)
|
164
|
+
)
|
165
|
+
|
166
|
+
@mod_nodes << node
|
167
|
+
end
|
168
|
+
|
169
|
+
node
|
170
|
+
end
|
171
|
+
|
172
|
+
def edge_for_nodes(src_node, dep_node, edges:, edge_type: :dependent)
|
173
|
+
edge = edges.find do |e|
|
174
|
+
e.src_node == src_node && e.dep_node == dep_node
|
175
|
+
end
|
176
|
+
if edge.nil?
|
177
|
+
edge = ChaosDetector::GraphTheory::Edge.new(src_node, dep_node, edge_type: edge_type)
|
178
|
+
edges << edge
|
179
|
+
end
|
180
|
+
edge
|
181
|
+
end
|
182
|
+
|
183
|
+
def perform_node_action(frame)
|
184
|
+
return unless [:call, :return].include?(frame.event)
|
185
|
+
node = fn_node_for_frame(frame)
|
186
|
+
|
187
|
+
ChaosUtils.with(node && frame.event == :return && frame.fn_info.fn_line) do |fn_line|
|
188
|
+
if !node.fn_line_end.nil? && node.fn_line_end != fn_line
|
189
|
+
end
|
190
|
+
node.fn_line_end = [fn_line, node.fn_line_end.to_i].max
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
def perform_edge_action(frame)
|
196
|
+
return unless frame.fn_info && frame.event==:call #&& frame.caller_info
|
197
|
+
|
198
|
+
dest_node = fn_node_for(frame.fn_info)
|
199
|
+
if dest_node.nil?
|
200
|
+
# unless @err_nodes.include?(dest_node)
|
201
|
+
# log "Couldn't find destination node (of #{@nodes.length} / #{@edges_call.length}) on #{frame}"
|
202
|
+
# @err_nodes << dest_node
|
203
|
+
# end
|
204
|
+
return
|
205
|
+
end
|
206
|
+
|
207
|
+
caller_node = fn_node_for(frame.caller_info)
|
208
|
+
if caller_node.nil?
|
209
|
+
caller_node = ChaosDetector::ChaosGraphs::FunctionNode.root_node
|
210
|
+
raise 'Caller node is required (falls back to root).' if caller_node.nil?
|
211
|
+
|
212
|
+
# log("Adding edge to root!")
|
213
|
+
@nodes << caller_node
|
214
|
+
end
|
215
|
+
|
216
|
+
edges = frame.event == :return ? @edges_ret : @edges_call
|
217
|
+
edge_for_nodes(caller_node, dest_node, edges: edges)
|
218
|
+
end
|
219
|
+
|
220
|
+
def perform_mod_edge_action(frame)
|
221
|
+
return unless frame.mod_info && frame.caller_info
|
222
|
+
|
223
|
+
caller_node = mod_node_from_info(frame.caller_info)
|
224
|
+
dest_node = mod_node_from_info(frame.mod_info)
|
225
|
+
edge_for_nodes(dest_node, caller_node, edges: @mod_edges, edge_type: frame.event)
|
226
|
+
end
|
227
|
+
|
228
|
+
def domain_from_path(local_path:)
|
229
|
+
key = domain_hash.keys.find { |k| local_path.start_with?(k) }
|
230
|
+
key ? domain_hash[key] : ChaosDetector::GraphTheory::Node::ROOT_NODE_NAME
|
231
|
+
end
|
232
|
+
|
233
|
+
def log(msg, **opts)
|
234
|
+
ChaosUtils.log_msg(msg, subject: 'Navigator', **opts)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|