seafoam 0.11 → 0.14
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 +4 -4
- data/bin/bgv2isabelle +3 -2
- data/bin/bgv2json +3 -2
- data/bin/seafoam +3 -2
- data/lib/seafoam/bgv/bgv_parser.rb +58 -50
- data/lib/seafoam/binary/io_binary_reader.rb +19 -17
- data/lib/seafoam/colors.rb +17 -11
- data/lib/seafoam/commands.rb +210 -202
- data/lib/seafoam/formatters/base.rb +2 -0
- data/lib/seafoam/formatters/formatters.rb +5 -3
- data/lib/seafoam/formatters/json.rb +8 -5
- data/lib/seafoam/formatters/text.rb +7 -4
- data/lib/seafoam/graal/graph_description.rb +10 -3
- data/lib/seafoam/graal/pi.rb +10 -6
- data/lib/seafoam/graal/source.rb +13 -9
- data/lib/seafoam/graph.rb +15 -9
- data/lib/seafoam/graphviz_writer.rb +129 -99
- data/lib/seafoam/isabelle_writer.rb +9 -7
- data/lib/seafoam/json_writer.rb +17 -13
- data/lib/seafoam/markdown_writer.rb +18 -0
- data/lib/seafoam/mermaid_writer.rb +52 -0
- data/lib/seafoam/passes/fallback.rb +10 -6
- data/lib/seafoam/passes/graal.rb +189 -161
- data/lib/seafoam/passes/truffle.rb +120 -22
- data/lib/seafoam/passes.rb +36 -29
- data/lib/seafoam/spotlight.rb +4 -2
- data/lib/seafoam/version.rb +3 -1
- data/lib/seafoam.rb +22 -20
- metadata +9 -40
- data/bin/cfg2asm +0 -20
- data/lib/seafoam/cfg/cfg_parser.rb +0 -93
- data/lib/seafoam/cfg/disassembler.rb +0 -70
@@ -1,15 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tsort"
|
4
|
+
|
1
5
|
module Seafoam
|
2
6
|
module Passes
|
3
7
|
# The Truffle pass applies if it looks like it was compiled by Truffle.
|
4
8
|
class TrufflePass < Pass
|
5
|
-
|
6
|
-
graph
|
7
|
-
|
9
|
+
class << self
|
10
|
+
def applies?(graph)
|
11
|
+
graph.props.values.any? do |v|
|
12
|
+
TRIGGERS.any? { |t| v.to_s.include?(t) }
|
13
|
+
end
|
8
14
|
end
|
9
15
|
end
|
10
16
|
|
11
17
|
def apply(graph)
|
12
|
-
simplify_truffle_args
|
18
|
+
simplify_truffle_args(graph) if @options[:simplify_truffle_args]
|
19
|
+
simplify_alloc(graph) if @options[:simplify_alloc]
|
13
20
|
end
|
14
21
|
|
15
22
|
private
|
@@ -18,41 +25,132 @@ module Seafoam
|
|
18
25
|
# like a Graal parameter node.
|
19
26
|
def simplify_truffle_args(graph)
|
20
27
|
graph.nodes.dup.each_value do |node|
|
21
|
-
next unless node.
|
28
|
+
next unless node.node_class == "org.graalvm.compiler.nodes.java.LoadIndexedNode"
|
22
29
|
|
23
|
-
index_node = node.inputs.find { |edge| edge.props[:name] ==
|
24
|
-
array_node = Graal::Pi.follow_pi_object(node.inputs.find { |edge| edge.props[:name] ==
|
30
|
+
index_node = node.inputs.find { |edge| edge.props[:name] == "index" }.from
|
31
|
+
array_node = Graal::Pi.follow_pi_object(node.inputs.find { |edge| edge.props[:name] == "array" }.from)
|
25
32
|
|
26
|
-
next unless index_node.
|
27
|
-
next unless array_node.
|
33
|
+
next unless index_node.node_class == "org.graalvm.compiler.nodes.ConstantNode"
|
34
|
+
next unless array_node.node_class == "org.graalvm.compiler.nodes.ParameterNode"
|
28
35
|
|
29
36
|
node.props[:truffle_arg_load] = true
|
30
37
|
|
31
|
-
index = index_node.props[
|
38
|
+
index = index_node.props["rawvalue"]
|
32
39
|
|
33
|
-
arg_node = graph.create_node(graph.new_id,
|
40
|
+
arg_node = graph.create_node(graph.new_id,
|
41
|
+
{ synthetic: true, inlined: true, label: "T(#{index})", kind: "input" })
|
34
42
|
|
35
43
|
node.outputs.each do |output|
|
36
|
-
next if output.props[:name] ==
|
44
|
+
next if output.props[:name] == "next"
|
37
45
|
|
38
|
-
graph.create_edge
|
39
|
-
graph.remove_edge
|
46
|
+
graph.create_edge(arg_node, output.to, output.props.dup)
|
47
|
+
graph.remove_edge(output)
|
40
48
|
end
|
41
49
|
end
|
42
50
|
|
43
51
|
graph.nodes.each_value.select { |node| node.props[:truffle_arg_load] }.each do |node|
|
44
|
-
control_in = node.inputs.find { |edge| edge.props[:name] ==
|
45
|
-
control_out = node.outputs.find { |edge| edge.props[:name] ==
|
46
|
-
graph.create_edge
|
47
|
-
graph.remove_edge
|
48
|
-
graph.remove_edge
|
52
|
+
control_in = node.inputs.find { |edge| edge.props[:name] == "next" }
|
53
|
+
control_out = node.outputs.find { |edge| edge.props[:name] == "next" }
|
54
|
+
graph.create_edge(control_in.from, control_out.to, { name: "next" })
|
55
|
+
graph.remove_edge(control_in)
|
56
|
+
graph.remove_edge(control_out)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Hide nodes that are uninteresting inputs to an allocation node. These
|
61
|
+
# are constants that are null or 0.
|
62
|
+
def simplify_alloc(graph)
|
63
|
+
commit_allocation_nodes = graph.nodes.each_value.select do |node|
|
64
|
+
node.node_class == "org.graalvm.compiler.nodes.virtual.CommitAllocationNode"
|
65
|
+
end
|
66
|
+
|
67
|
+
commit_allocation_nodes.each do |commit_allocation_node|
|
68
|
+
control_flow_pred = commit_allocation_node.inputs.first
|
69
|
+
control_flow_next = commit_allocation_node.outputs.first
|
70
|
+
|
71
|
+
objects = []
|
72
|
+
virtual_to_object = {}
|
73
|
+
|
74
|
+
# First step to fill virtual_to_object and avoid ordering issues
|
75
|
+
commit_allocation_node.props.each_pair do |key, value|
|
76
|
+
m = /^object\((\d+)\)$/.match(key)
|
77
|
+
next unless m
|
78
|
+
|
79
|
+
virtual_id = m[1].to_i
|
80
|
+
|
81
|
+
(m = /^(\w+(?:\[\])?)\[([0-9,]+)\]$/.match(value)) || raise(value)
|
82
|
+
class_name, values = m.captures
|
83
|
+
values = values.split(",").map(&:to_i)
|
84
|
+
virtual_node = graph.nodes[virtual_id]
|
85
|
+
if virtual_node.node_class == "org.graalvm.compiler.nodes.virtual.VirtualArrayNode"
|
86
|
+
label = "New #{class_name[0...-1]}#{virtual_node.props["length"]}]"
|
87
|
+
fields = values.size.times.to_a
|
88
|
+
else
|
89
|
+
label = "New #{class_name}"
|
90
|
+
fields = virtual_node.props["fields"].map { |field| field[:name] }
|
91
|
+
end
|
92
|
+
raise unless fields.size == values.size
|
93
|
+
|
94
|
+
new_node = graph.create_node(graph.new_id, { synthetic: true, label: label, kind: "alloc" })
|
95
|
+
|
96
|
+
object = [new_node, virtual_node, fields, values]
|
97
|
+
objects << object
|
98
|
+
virtual_to_object[virtual_id] = object
|
99
|
+
end
|
100
|
+
|
101
|
+
# Topological sort of the new nodes in the control flow according to data dependencies
|
102
|
+
# There can be cycles (e.g., instances referring one another),
|
103
|
+
# so we use TSort.strongly_connected_components instead of TSort.tsort.
|
104
|
+
objects = TSort.strongly_connected_components(
|
105
|
+
objects.method(:each),
|
106
|
+
lambda do |(_new_node, _virtual_node, _fields, values), &b|
|
107
|
+
values.each do |value_id|
|
108
|
+
usage = virtual_to_object[value_id]
|
109
|
+
b.call(usage) if usage
|
110
|
+
end
|
111
|
+
end
|
112
|
+
).reduce(:concat)
|
113
|
+
|
114
|
+
prev = control_flow_pred.from
|
115
|
+
objects.each do |new_node, virtual_node, fields, values|
|
116
|
+
graph.create_edge(prev, new_node, control_flow_pred.props)
|
117
|
+
|
118
|
+
allocated_object_node = virtual_node.outputs.find do |output|
|
119
|
+
output.to.node_class == "org.graalvm.compiler.nodes.virtual.AllocatedObjectNode"
|
120
|
+
end
|
121
|
+
if allocated_object_node
|
122
|
+
allocated_object_node = allocated_object_node.to
|
123
|
+
|
124
|
+
allocated_object_node.outputs.each do |edge|
|
125
|
+
graph.create_edge(new_node, edge.to, edge.props)
|
126
|
+
end
|
127
|
+
|
128
|
+
allocated_object_node.props[:hidden] = true
|
129
|
+
end
|
130
|
+
|
131
|
+
fields.zip(values) do |field, value_id|
|
132
|
+
value_node = virtual_to_object[value_id]&.first || graph.nodes[value_id]
|
133
|
+
if @options[:hide_null_fields] &&
|
134
|
+
(value_node.node_class == "org.graalvm.compiler.nodes.ConstantNode") &&
|
135
|
+
["Object[null]", "0"].include?(value_node.props["rawvalue"])
|
136
|
+
value_node.props[:hidden] = true
|
137
|
+
else
|
138
|
+
graph.create_edge(value_node, new_node, { name: field })
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
virtual_node.props[:hidden] = true
|
143
|
+
|
144
|
+
prev = new_node
|
145
|
+
end
|
146
|
+
graph.create_edge(prev, control_flow_next.to, control_flow_next.props)
|
147
|
+
|
148
|
+
commit_allocation_node.props[:hidden] = true
|
49
149
|
end
|
50
150
|
end
|
51
151
|
|
52
152
|
# If we see these in the graph properties it's probably a Truffle graph.
|
53
|
-
TRIGGERS =
|
54
|
-
TruffleCompiler
|
55
|
-
]
|
153
|
+
TRIGGERS = ["TruffleCompiler"]
|
56
154
|
end
|
57
155
|
end
|
58
156
|
end
|
data/lib/seafoam/passes.rb
CHANGED
@@ -1,39 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seafoam
|
2
4
|
# Passes are routines to read the graph and apply properties which tools,
|
3
5
|
# such as the render command, can use to show more understandable output.
|
4
6
|
module Passes
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
class << self
|
8
|
+
# Apply all applicable passes to a graph.
|
9
|
+
def apply(graph, options = {})
|
10
|
+
passes.each do |pass|
|
11
|
+
next unless pass.applies?(graph)
|
12
|
+
|
13
|
+
# Record for information that the pass was applied this graph.
|
14
|
+
passes_applied = graph.props[:passes_applied] ||= []
|
15
|
+
passes_applied.push(pass)
|
16
|
+
|
17
|
+
# Run the pass.
|
18
|
+
instance = pass.new(options)
|
19
|
+
instance.apply(graph)
|
20
|
+
end
|
17
21
|
end
|
18
|
-
end
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
# Get a list of all passes in the system.
|
24
|
+
def passes
|
25
|
+
# We have a defined order for passes to run - these passes at the start.
|
26
|
+
pre_passes = [
|
27
|
+
TrufflePass,
|
28
|
+
GraalPass,
|
29
|
+
]
|
27
30
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
31
|
+
# The fallback pass runs last.
|
32
|
+
post_passes = [
|
33
|
+
FallbackPass,
|
34
|
+
]
|
32
35
|
|
33
|
-
|
34
|
-
|
36
|
+
# Any extra passes in the middle.
|
37
|
+
extra_passes = Pass::SUBCLASSES.dup - pre_passes - post_passes
|
35
38
|
|
36
|
-
|
39
|
+
pre_passes + extra_passes + post_passes
|
40
|
+
end
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
@@ -54,8 +58,11 @@ module Seafoam
|
|
54
58
|
raise NotImplementedError
|
55
59
|
end
|
56
60
|
|
57
|
-
|
58
|
-
|
61
|
+
class << self
|
62
|
+
def inherited(pass)
|
63
|
+
super
|
64
|
+
SUBCLASSES.push(pass)
|
65
|
+
end
|
59
66
|
end
|
60
67
|
end
|
61
68
|
end
|
data/lib/seafoam/spotlight.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seafoam
|
2
4
|
# Spotlight can *light* nodes, which makes them visible, their adjacent nodes
|
3
5
|
# visible by grey, and other nodes invisible. Multiple nodes can be *lit*.
|
@@ -9,11 +11,11 @@ module Seafoam
|
|
9
11
|
# Mark a node as lit by the spotlight.
|
10
12
|
def light(node)
|
11
13
|
# This node is lit.
|
12
|
-
node.props[:spotlight] =
|
14
|
+
node.props[:spotlight] = "lit"
|
13
15
|
|
14
16
|
# Adjacent nodes are shaded, if they haven't be lit themselvs.
|
15
17
|
node.adjacent.each do |adjacent|
|
16
|
-
adjacent.props[:spotlight] ||=
|
18
|
+
adjacent.props[:spotlight] ||= "shaded"
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
data/lib/seafoam/version.rb
CHANGED
data/lib/seafoam.rb
CHANGED
@@ -1,20 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "seafoam/version"
|
4
|
+
require "seafoam/binary/io_binary_reader"
|
5
|
+
require "seafoam/bgv/bgv_parser"
|
6
|
+
require "seafoam/colors"
|
7
|
+
require "seafoam/graph"
|
8
|
+
require "seafoam/graal/graph_description"
|
9
|
+
require "seafoam/graal/source"
|
10
|
+
require "seafoam/graal/pi"
|
11
|
+
require "seafoam/passes"
|
12
|
+
require "seafoam/passes/truffle"
|
13
|
+
require "seafoam/passes/graal"
|
14
|
+
require "seafoam/passes/fallback"
|
15
|
+
require "seafoam/spotlight"
|
16
|
+
require "seafoam/isabelle_writer"
|
17
|
+
require "seafoam/json_writer"
|
18
|
+
require "seafoam/graphviz_writer"
|
19
|
+
require "seafoam/mermaid_writer"
|
20
|
+
require "seafoam/markdown_writer"
|
21
|
+
require "seafoam/commands"
|
22
|
+
require "seafoam/formatters/formatters"
|
metadata
CHANGED
@@ -1,43 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: seafoam
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.14'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Seaton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-09-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: crabstone
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '4.0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '4.0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: benchmark-ips
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '2.7'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '2.7'
|
41
13
|
- !ruby/object:Gem::Dependency
|
42
14
|
name: rake
|
43
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,38 +39,34 @@ dependencies:
|
|
67
39
|
- !ruby/object:Gem::Version
|
68
40
|
version: '3.8'
|
69
41
|
- !ruby/object:Gem::Dependency
|
70
|
-
name: rubocop
|
42
|
+
name: rubocop-shopify
|
71
43
|
requirement: !ruby/object:Gem::Requirement
|
72
44
|
requirements:
|
73
45
|
- - "~>"
|
74
46
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
47
|
+
version: 2.9.0
|
76
48
|
type: :development
|
77
49
|
prerelease: false
|
78
50
|
version_requirements: !ruby/object:Gem::Requirement
|
79
51
|
requirements:
|
80
52
|
- - "~>"
|
81
53
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
54
|
+
version: 2.9.0
|
83
55
|
description:
|
84
56
|
email:
|
85
57
|
executables:
|
86
58
|
- seafoam
|
87
59
|
- bgv2json
|
88
60
|
- bgv2isabelle
|
89
|
-
- cfg2asm
|
90
61
|
extensions: []
|
91
62
|
extra_rdoc_files: []
|
92
63
|
files:
|
93
64
|
- bin/bgv2isabelle
|
94
65
|
- bin/bgv2json
|
95
|
-
- bin/cfg2asm
|
96
66
|
- bin/seafoam
|
97
67
|
- lib/seafoam.rb
|
98
68
|
- lib/seafoam/bgv/bgv_parser.rb
|
99
69
|
- lib/seafoam/binary/io_binary_reader.rb
|
100
|
-
- lib/seafoam/cfg/cfg_parser.rb
|
101
|
-
- lib/seafoam/cfg/disassembler.rb
|
102
70
|
- lib/seafoam/colors.rb
|
103
71
|
- lib/seafoam/commands.rb
|
104
72
|
- lib/seafoam/formatters/base.rb
|
@@ -112,6 +80,8 @@ files:
|
|
112
80
|
- lib/seafoam/graphviz_writer.rb
|
113
81
|
- lib/seafoam/isabelle_writer.rb
|
114
82
|
- lib/seafoam/json_writer.rb
|
83
|
+
- lib/seafoam/markdown_writer.rb
|
84
|
+
- lib/seafoam/mermaid_writer.rb
|
115
85
|
- lib/seafoam/passes.rb
|
116
86
|
- lib/seafoam/passes/fallback.rb
|
117
87
|
- lib/seafoam/passes/graal.rb
|
@@ -130,15 +100,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
130
100
|
requirements:
|
131
101
|
- - ">="
|
132
102
|
- !ruby/object:Gem::Version
|
133
|
-
version: 2.
|
103
|
+
version: 2.7.0
|
134
104
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
105
|
requirements:
|
136
106
|
- - ">="
|
137
107
|
- !ruby/object:Gem::Version
|
138
108
|
version: '0'
|
139
109
|
requirements: []
|
140
|
-
|
141
|
-
rubygems_version: 2.7.6.3
|
110
|
+
rubygems_version: 3.3.7
|
142
111
|
signing_key:
|
143
112
|
specification_version: 4
|
144
113
|
summary: A tool for working with compiler graphs
|
data/bin/cfg2asm
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'seafoam'
|
4
|
-
|
5
|
-
# This is the 'cfg2asm' command line entry point.
|
6
|
-
|
7
|
-
begin
|
8
|
-
# Run the command line.
|
9
|
-
commands = Seafoam::Commands.new($stdout)
|
10
|
-
commands.cfg2asm(*ARGV)
|
11
|
-
rescue StandardError => e
|
12
|
-
if $DEBUG
|
13
|
-
# Re-raise the exception so the user sees it, if debugging is
|
14
|
-
# enabled (ruby -d).
|
15
|
-
raise e
|
16
|
-
else
|
17
|
-
# Otherwise, just print the message.
|
18
|
-
warn "seafoam: #{e.message}"
|
19
|
-
end
|
20
|
-
end
|
@@ -1,93 +0,0 @@
|
|
1
|
-
require 'stringio'
|
2
|
-
require 'zlib'
|
3
|
-
|
4
|
-
module Seafoam
|
5
|
-
module CFG
|
6
|
-
Code = Struct.new(:arch, :arch_width, :base, :code)
|
7
|
-
Comment = Struct.new(:offset, :comment)
|
8
|
-
NMethod = Struct.new(:code, :comments)
|
9
|
-
|
10
|
-
# A parser for CFG files.
|
11
|
-
class CFGParser
|
12
|
-
def initialize(out, file)
|
13
|
-
@out = out
|
14
|
-
data = File.read(file, encoding: Encoding::ASCII_8BIT)
|
15
|
-
if data[0..1].bytes == [0x1f, 0x8b]
|
16
|
-
data = Zlib.gunzip(data)
|
17
|
-
end
|
18
|
-
@reader = StringIO.new(data)
|
19
|
-
@state = :any
|
20
|
-
@cfg_name = nil
|
21
|
-
end
|
22
|
-
|
23
|
-
def skip_over_cfg(name)
|
24
|
-
loop do
|
25
|
-
line = @reader.readline("\n")
|
26
|
-
case line
|
27
|
-
when "begin_cfg\n"
|
28
|
-
@state = :cfg
|
29
|
-
@cfg_name = nil
|
30
|
-
when / name "(.*)"\n/
|
31
|
-
if @state == :cfg
|
32
|
-
@cfg_name = Regexp.last_match(1)
|
33
|
-
end
|
34
|
-
when "end_cfg\n"
|
35
|
-
raise unless @state == :cfg
|
36
|
-
|
37
|
-
@state = :any
|
38
|
-
break if @cfg_name == name
|
39
|
-
else
|
40
|
-
next
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def read_nmethod
|
46
|
-
raise unless @state == :any
|
47
|
-
|
48
|
-
arch = nil
|
49
|
-
arch_width = nil
|
50
|
-
code = nil
|
51
|
-
comments = []
|
52
|
-
raise unless @reader.readline == "begin_nmethod\n"
|
53
|
-
|
54
|
-
loop do
|
55
|
-
line = @reader.readline("\n")
|
56
|
-
case line
|
57
|
-
when / Platform (.*) (.*) <\|\|@\n/
|
58
|
-
arch = Regexp.last_match(1)
|
59
|
-
arch_width = Regexp.last_match(2)
|
60
|
-
when / HexCode (.*) (.*) <\|\|@\n/
|
61
|
-
base = Regexp.last_match(1).to_i(16)
|
62
|
-
code = [Regexp.last_match(2)].pack('H*')
|
63
|
-
raise if arch.nil? || arch_width.nil?
|
64
|
-
|
65
|
-
code = Code.new(arch, arch_width, base, code)
|
66
|
-
when / Comment (\d*) (.*) <\|\|@\n/
|
67
|
-
offset = Regexp.last_match(1).to_i
|
68
|
-
comment = Regexp.last_match(2)
|
69
|
-
comments.push Comment.new(offset, comment)
|
70
|
-
when " <<<HexCodeFile\n"
|
71
|
-
next
|
72
|
-
when " HexCodeFile>>> <|@\n"
|
73
|
-
next
|
74
|
-
when "end_nmethod\n"
|
75
|
-
break
|
76
|
-
when / (.*) <\|\|@\n/
|
77
|
-
offset = -1
|
78
|
-
comment = Regexp.last_match(1)
|
79
|
-
comments.push Comment.new(offset, comment)
|
80
|
-
when / (.*)\n/
|
81
|
-
offset = -1
|
82
|
-
comment = Regexp.last_match(1)
|
83
|
-
comments.push Comment.new(offset, comment)
|
84
|
-
else
|
85
|
-
# In case anything was missed
|
86
|
-
raise 'There is currently no case for this line. Please open an issue so it can be addressed.'
|
87
|
-
end
|
88
|
-
end
|
89
|
-
NMethod.new(code, comments)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
@@ -1,70 +0,0 @@
|
|
1
|
-
require 'stringio'
|
2
|
-
|
3
|
-
module Seafoam
|
4
|
-
module CFG
|
5
|
-
# Disassemble and print comments from cfg files
|
6
|
-
class Disassembler
|
7
|
-
def initialize(out)
|
8
|
-
@out = out
|
9
|
-
end
|
10
|
-
|
11
|
-
def disassemble(nmethod, print_comments)
|
12
|
-
require_crabstone
|
13
|
-
|
14
|
-
comments = nmethod.comments
|
15
|
-
comments_n = 0
|
16
|
-
|
17
|
-
case [nmethod.code.arch, nmethod.code.arch_width]
|
18
|
-
when %w[AMD64 64]
|
19
|
-
crabstone_arch = [Crabstone::ARCH_X86, Crabstone::MODE_64]
|
20
|
-
else
|
21
|
-
raise "Unknown architecture #{nmethod.code.arch} and bit width #{nmethod.code.arch_width}"
|
22
|
-
end
|
23
|
-
|
24
|
-
cs = Crabstone::Disassembler.new(*crabstone_arch)
|
25
|
-
begin
|
26
|
-
cs.disasm(nmethod.code.code, nmethod.code.base).each do |i|
|
27
|
-
if print_comments
|
28
|
-
# Print comments associated to the instruction.
|
29
|
-
last_comment = i.address + i.bytes.length - nmethod.code.base
|
30
|
-
while comments_n < comments.length && comments[comments_n].offset < last_comment
|
31
|
-
if comments[comments_n].offset == -1
|
32
|
-
@out.printf("\t\t\t\t;%<comment>s\n", comment: comments[comments_n].comment)
|
33
|
-
else
|
34
|
-
@out.printf(
|
35
|
-
"\t\t\t\t;Comment %<loc>i:\t%<comment>s\n",
|
36
|
-
loc: comments[comments_n].offset,
|
37
|
-
comment: comments[comments_n].comment
|
38
|
-
)
|
39
|
-
end
|
40
|
-
comments_n += 1
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# Print the instruction.
|
45
|
-
@out.printf(
|
46
|
-
"\t0x%<address>x:\t%<instruction>s\t%<details>s\n",
|
47
|
-
address: i.address,
|
48
|
-
instruction: i.mnemonic,
|
49
|
-
details: i.op_str
|
50
|
-
)
|
51
|
-
end
|
52
|
-
rescue StandardError => e
|
53
|
-
raise "Disassembly error: #{e.message}"
|
54
|
-
ensure
|
55
|
-
cs.close
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def require_crabstone
|
60
|
-
require 'crabstone'
|
61
|
-
rescue LoadError => e
|
62
|
-
if $DEBUG
|
63
|
-
raise e
|
64
|
-
else
|
65
|
-
raise 'Could not load Capstone - is it installed?'
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|