seafoam 0.8 → 0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/bin/bgv2isabelle +1 -5
  3. data/bin/bgv2json +1 -5
  4. data/bin/seafoam +1 -5
  5. data/lib/seafoam/bgv/bgv_parser.rb +1 -2
  6. data/lib/seafoam/commands.rb +135 -101
  7. data/lib/seafoam/formatters/base.rb +88 -0
  8. data/lib/seafoam/formatters/formatters.rb +8 -0
  9. data/lib/seafoam/formatters/json.rb +82 -0
  10. data/lib/seafoam/formatters/text.rb +70 -0
  11. data/lib/seafoam/graal/graph_description.rb +22 -0
  12. data/lib/seafoam/graal/pi.rb +18 -0
  13. data/lib/seafoam/graal/source.rb +5 -9
  14. data/lib/seafoam/graph.rb +9 -1
  15. data/lib/seafoam/graphviz_writer.rb +3 -0
  16. data/lib/seafoam/json_writer.rb +1 -3
  17. data/lib/seafoam/{annotators → passes}/fallback.rb +4 -4
  18. data/lib/seafoam/{annotators → passes}/graal.rb +43 -15
  19. data/lib/seafoam/passes/truffle.rb +58 -0
  20. data/lib/seafoam/passes.rb +61 -0
  21. data/lib/seafoam/version.rb +1 -1
  22. data/lib/seafoam.rb +7 -6
  23. metadata +17 -81
  24. data/.github/probots.yml +0 -2
  25. data/.github/workflows/workflows.yml +0 -40
  26. data/.gitignore +0 -6
  27. data/.rubocop.yml +0 -37
  28. data/.ruby-version +0 -1
  29. data/.seafoam/config +0 -1
  30. data/CODE_OF_CONDUCT.md +0 -128
  31. data/CONTRIBUTING.md +0 -5
  32. data/Gemfile +0 -2
  33. data/Gemfile.lock +0 -59
  34. data/LICENSE.md +0 -7
  35. data/README.md +0 -378
  36. data/bin/cfg2asm +0 -24
  37. data/demos/box-unbox-stats +0 -65
  38. data/docs/annotators.md +0 -43
  39. data/docs/bgv.md +0 -294
  40. data/docs/getting-graphs.md +0 -63
  41. data/docs/images/igv.png +0 -0
  42. data/docs/images/seafoam.png +0 -0
  43. data/docs/images/spotlight-igv.png +0 -0
  44. data/docs/images/spotlight-seafoam.png +0 -0
  45. data/docs/json.md +0 -35
  46. data/examples/Fib.java +0 -24
  47. data/examples/MatMult.java +0 -39
  48. data/examples/fib-java.bgv.gz +0 -0
  49. data/examples/fib.js +0 -15
  50. data/examples/fib.rb +0 -15
  51. data/examples/identity.rb +0 -13
  52. data/examples/java/Irreducible.class +0 -0
  53. data/examples/java/Irreducible.j +0 -35
  54. data/examples/java/IrreducibleDecompiled.java +0 -21
  55. data/examples/java/JavaExamples.java +0 -418
  56. data/examples/matmult.rb +0 -29
  57. data/examples/overflow.rb +0 -13
  58. data/examples/ruby/clamps.rb +0 -20
  59. data/examples/ruby/graal.patch +0 -15
  60. data/examples/ruby/ruby_examples.rb +0 -278
  61. data/lib/seafoam/annotators.rb +0 -54
  62. data/lib/seafoam/cfg/cfg_parser.rb +0 -93
  63. data/lib/seafoam/cfg/disassembler.rb +0 -70
  64. data/lib/seafoam/config.rb +0 -34
  65. data/seafoam.gemspec +0 -22
  66. data/spec/seafoam/annotators/fallback_spec.rb +0 -69
  67. data/spec/seafoam/annotators/graal_spec.rb +0 -96
  68. data/spec/seafoam/annotators_spec.rb +0 -61
  69. data/spec/seafoam/bgv/bgv_parser_spec.rb +0 -167
  70. data/spec/seafoam/binary/io_binary_reader_spec.rb +0 -176
  71. data/spec/seafoam/cfg/cfg_parser_spec.rb +0 -21
  72. data/spec/seafoam/cfg/disassembler_spec.rb +0 -32
  73. data/spec/seafoam/command_spec.rb +0 -314
  74. data/spec/seafoam/graph_spec.rb +0 -172
  75. data/spec/seafoam/graphviz_writer_spec.rb +0 -63
  76. data/spec/seafoam/json_writer_spec.rb +0 -14
  77. data/spec/seafoam/spec_helpers.rb +0 -34
  78. data/spec/seafoam/spotlight_spec.rb +0 -38
  79. data/tools/render-all +0 -36
@@ -1,278 +0,0 @@
1
- # truffleruby_primitives: true
2
-
3
- # We're using Graal with graal.patch, to disable some inlining.
4
-
5
- # % ruby --jvm --experimental-options --engine.OSR=false --engine.MultiTier=false --engine.TraceCompilation --vm.Dgraal.Dump=Truffle:1 ruby_examples.rb
6
-
7
- RANDOM = Random.new
8
- EXCEPTION = Exception.new
9
-
10
- def example_local_variables(x, y)
11
- a = x + y
12
- a * 2 + a
13
- end
14
-
15
- def example_local_variables_state(x, y)
16
- a = x + y
17
- opaque_call
18
- a * 2 + a
19
- end
20
-
21
- def example_arith_operator(x, y)
22
- x + y
23
- end
24
-
25
- def example_compare_operator(x, y)
26
- x <= y
27
- end
28
-
29
- def example_phi(condition, x)
30
- if condition
31
- a = opaque_call_a
32
- else
33
- a = opaque_call_b
34
- end
35
- a + x
36
- end
37
-
38
- def example_simple_call(object, x)
39
- object.instance_call(x)
40
- end
41
-
42
- def example_stamp(x)
43
- x & 0x1234
44
- end
45
-
46
- def example_full_escape(x)
47
- a = [x]
48
- Primitive.blackhole a
49
- a[0]
50
- end
51
-
52
- def example_no_escape(x)
53
- a = [x]
54
- a[0]
55
- end
56
-
57
- def example_partial_escape(condition, x)
58
- a = [x]
59
- if condition
60
- Primitive.blackhole a
61
- a[0]
62
- else
63
- a[0]
64
- end
65
- end
66
-
67
- def example_if(condition, x, y)
68
- if condition
69
- Primitive.blackhole x
70
- a = x
71
- else
72
- Primitive.blackhole y
73
- a = y
74
- end
75
- a
76
- end
77
-
78
- def example_if_never_taken(condition, x, y)
79
- if condition
80
- Primitive.blackhole x
81
- a = x
82
- else
83
- Primitive.blackhole y
84
- a = y
85
- end
86
- a
87
- end
88
-
89
- def example_int_switch(value, x, y, z)
90
- case value
91
- when 0
92
- Primitive.blackhole x
93
- a = x
94
- when 1
95
- Primitive.blackhole y
96
- a = y
97
- else
98
- Primitive.blackhole z
99
- a = z
100
- end
101
- a
102
- end
103
-
104
- def example_string_switch(value, x, y, z)
105
- case value
106
- when 'foo'
107
- Primitive.blackhole x
108
- a = x
109
- when 'bar'
110
- Primitive.blackhole y
111
- a = y
112
- else
113
- Primitive.blackhole z
114
- a = z
115
- end
116
- a
117
- end
118
-
119
- def example_while(count)
120
- a = count
121
- while a > 0
122
- Primitive.blackhole a
123
- a -= 1
124
- end
125
- count
126
- end
127
-
128
- def example_for(count)
129
- count.times do |a|
130
- Primitive.blackhole a
131
- end
132
- end
133
-
134
- def example_nested_while(count)
135
- a = count
136
- while (a > 0)
137
- y = count
138
- while (y > 0)
139
- Primitive.blackhole a
140
- y -= 1
141
- end
142
- a -= 1
143
- end
144
- count
145
- end
146
-
147
- def example_while_break(count)
148
- a = count
149
- while a > 0
150
- if a == 4
151
- break
152
- end
153
- Primitive.blackhole a
154
- a -= 1
155
- end
156
- count
157
- end
158
-
159
- def example_raise
160
- raise EXCEPTION
161
- end
162
-
163
- def example_rescue
164
- begin
165
- opaque_raise
166
- rescue Exception => e
167
- Primitive.blackhole e
168
- end
169
- end
170
-
171
- def example_raise_rescue
172
- begin
173
- raise EXCEPTION
174
- rescue Exception => e
175
- Primitive.blackhole e
176
- end
177
- end
178
-
179
- def example_object_allocation(x)
180
- ExampleObject.new(x)
181
- end
182
-
183
- def example_array_allocation(x, y)
184
- [x, y]
185
- end
186
-
187
- def example_field_write(object, x)
188
- object.x = x
189
- end
190
-
191
- def example_field_read(object)
192
- object.x
193
- end
194
-
195
- def example_array_write(array, n, x)
196
- array[n] = x
197
- end
198
-
199
- def example_array_read(array, n)
200
- array[n]
201
- end
202
-
203
- def example_instance_of(x)
204
- x.is_a?(ExampleObject)
205
- end
206
-
207
- # no inline
208
- def opaque_call
209
- RANDOM.rand(1000)
210
- end
211
-
212
- # no inline
213
- def opaque_call_a
214
- opaque_call
215
- end
216
-
217
- # no inline
218
- def opaque_call_b
219
- opaque_call
220
- end
221
-
222
- # no inline
223
- def opaque_raise
224
- raise EXCEPTION
225
- end
226
-
227
- # no inline
228
- def static_call(x)
229
- x
230
- end
231
-
232
- class ExampleObject
233
- attr_accessor :x
234
-
235
- def initialize(x)
236
- @x = x
237
- end
238
-
239
- # no inline
240
- def instance_call(y)
241
- @x + y
242
- end
243
- end
244
-
245
- loop do
246
- example_local_variables RANDOM.rand(1000), RANDOM.rand(1000)
247
- example_local_variables_state RANDOM.rand(1000), RANDOM.rand(1000)
248
- example_arith_operator RANDOM.rand(1000), RANDOM.rand(1000)
249
- example_compare_operator RANDOM.rand(1000), RANDOM.rand(1000)
250
- example_phi [true, false].sample, RANDOM.rand(1000)
251
- example_simple_call ExampleObject.new(RANDOM.rand(1000)), RANDOM.rand(1000)
252
- example_stamp RANDOM.rand(1000)
253
- example_full_escape RANDOM.rand(1000)
254
- example_no_escape RANDOM.rand(1000)
255
- example_partial_escape [true, false].sample, RANDOM.rand(1000)
256
- example_if [true, false].sample, RANDOM.rand(1000), RANDOM.rand(1000)
257
- example_if_never_taken false, RANDOM.rand(1000), RANDOM.rand(1000)
258
- example_int_switch RANDOM.rand(3), RANDOM.rand(1000), RANDOM.rand(1000), RANDOM.rand(1000)
259
- example_string_switch ['foo', 'bar', 'baz'].sample, RANDOM.rand(1000), RANDOM.rand(1000), RANDOM.rand(1000)
260
- example_while RANDOM.rand(10)
261
- example_for RANDOM.rand(10)
262
- example_nested_while RANDOM.rand(10)
263
- example_while_break RANDOM.rand(10)
264
- begin
265
- example_raise
266
- rescue Exception => e
267
- #
268
- end
269
- example_rescue
270
- example_raise_rescue
271
- example_object_allocation RANDOM.rand(1000)
272
- example_array_allocation RANDOM.rand(1000), RANDOM.rand(1000)
273
- example_field_write ExampleObject.new(RANDOM.rand(1000)), RANDOM.rand(1000)
274
- example_field_read ExampleObject.new(RANDOM.rand(1000))
275
- example_array_write [RANDOM.rand(1000), RANDOM.rand(1000), RANDOM.rand(1000)], RANDOM.rand(3), RANDOM.rand(1000)
276
- example_array_read [RANDOM.rand(1000), RANDOM.rand(1000), RANDOM.rand(1000)], RANDOM.rand(3)
277
- example_instance_of [Object.new, ExampleObject.new(0)].sample
278
- end
@@ -1,54 +0,0 @@
1
- module Seafoam
2
- # Annotators are routines to read the graph and apply properties which tools,
3
- # such as the render command, can use to show more understandable output.
4
- module Annotators
5
- # Apply all applicable annotators to a graph.
6
- def self.apply(graph, options = {})
7
- annotators.each do |annotator|
8
- next unless annotator.applies?(graph)
9
-
10
- # Record for information that the annotator annotated this graph.
11
- annotated_by = graph.props[:annotated_by] ||= []
12
- annotated_by.push annotator
13
-
14
- # Run the annotator.
15
- instance = annotator.new(options)
16
- instance.annotate graph
17
- end
18
- end
19
-
20
- # Get a list of all annotators in the system.
21
- def self.annotators
22
- # Get all subclasses of Annotator.
23
- annotators = Annotator::SUBCLASSES.dup
24
-
25
- # We want the FallbackAnnotator to run last.
26
- annotators.delete FallbackAnnotator
27
- annotators.push FallbackAnnotator
28
-
29
- annotators
30
- end
31
- end
32
-
33
- # The base class for all annotators. You must subclass this to be recognized
34
- # as an annotator.
35
- class Annotator
36
- SUBCLASSES = []
37
-
38
- def initialize(options = {})
39
- @options = options
40
- end
41
-
42
- def applies?(_graph)
43
- raise NotImplementedError
44
- end
45
-
46
- def annotate(_graph)
47
- raise NotImplementedError
48
- end
49
-
50
- def self.inherited(annotator)
51
- SUBCLASSES.push annotator
52
- end
53
- end
54
- 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
@@ -1,34 +0,0 @@
1
- module Seafoam
2
- # Finds and loads configuration.
3
- class Config
4
- def initialize
5
- @dot_dir = find_dot_dir
6
- end
7
-
8
- # Load the configuration.
9
- def load_config
10
- config_file = File.expand_path('config', @dot_dir)
11
- if File.exist?(config_file)
12
- puts "loading config #{config_file}" if $DEBUG
13
- load config_file
14
- end
15
- end
16
-
17
- private
18
-
19
- # Walk up the directory chain from the current directory to root, looking
20
- # for .seafoam.
21
- def find_dot_dir
22
- dir = Dir.getwd
23
- loop do
24
- dot_dir = File.expand_path('.seafoam', dir)
25
- return dot_dir if Dir.exist?(dot_dir)
26
-
27
- new_dir = File.expand_path('..', dir)
28
- break if new_dir == dir
29
-
30
- dir = new_dir
31
- end
32
- end
33
- end
34
- end
data/seafoam.gemspec DELETED
@@ -1,22 +0,0 @@
1
- require_relative 'lib/seafoam/version'
2
-
3
- Gem::Specification.new do |spec|
4
- spec.name = 'seafoam'
5
- spec.version = Seafoam::VERSION
6
- spec.summary = 'A tool for working with compiler graphs'
7
- spec.authors = ['Chris Seaton']
8
- spec.homepage = 'https://github.com/Shopify/seafoam'
9
- spec.license = 'MIT'
10
-
11
- spec.files = `git ls-files`.split("\n").reject { |f| f.end_with?('.bgv') || f.end_with?('.cfg') }
12
- spec.bindir = 'bin'
13
- spec.executables = %w[seafoam bgv2json bgv2isabelle cfg2asm]
14
-
15
- spec.required_ruby_version = '>= 2.5.8'
16
-
17
- spec.add_dependency 'crabstone', '~> 4.0'
18
-
19
- spec.add_development_dependency 'benchmark-ips', '~> 2.7'
20
- spec.add_development_dependency 'rspec', '~> 3.8'
21
- spec.add_development_dependency 'rubocop', '~> 0.74'
22
- end
@@ -1,69 +0,0 @@
1
- require 'seafoam'
2
-
3
- require 'rspec'
4
-
5
- describe Seafoam::Annotators::FallbackAnnotator do
6
- it 'always applies' do
7
- expect(Seafoam::Annotators::FallbackAnnotator.applies?(Seafoam::Graph.new)).to be true
8
- end
9
-
10
- it 'adds a label annotation when there is a label property' do
11
- graph = Seafoam::Graph.new
12
- node = graph.create_node(0, 'label' => 'foo')
13
- annotator = Seafoam::Annotators::FallbackAnnotator.new
14
- annotator.annotate graph
15
- expect(node.props[:label]).to eq 'foo'
16
- end
17
-
18
- it 'does not overwrite an existing label annotation' do
19
- graph = Seafoam::Graph.new
20
- node = graph.create_node(0, 'label' => 'foo', :label => 'bar')
21
- annotator = Seafoam::Annotators::FallbackAnnotator.new
22
- annotator.annotate graph
23
- expect(node.props[:label]).to eq 'bar'
24
- end
25
-
26
- it 'adds nothing when there is no label property' do
27
- graph = Seafoam::Graph.new
28
- node = graph.create_node(0, 'xlabel' => 'foo')
29
- annotator = Seafoam::Annotators::FallbackAnnotator.new
30
- annotator.annotate graph
31
- expect(node.props[:label]).to be nil
32
- end
33
-
34
- it 'sets node kind to other when there is no kind' do
35
- graph = Seafoam::Graph.new
36
- node = graph.create_node(0)
37
- annotator = Seafoam::Annotators::FallbackAnnotator.new
38
- annotator.annotate graph
39
- expect(node.props[:kind]).to eq 'other'
40
- end
41
-
42
- it 'does not overwrite an existing node kind annotations' do
43
- graph = Seafoam::Graph.new
44
- node = graph.create_node(0, kind: 'control')
45
- annotator = Seafoam::Annotators::FallbackAnnotator.new
46
- annotator.annotate graph
47
- expect(node.props[:kind]).to eq 'control'
48
- end
49
-
50
- it 'sets edge kind to other when there is no kind' do
51
- graph = Seafoam::Graph.new
52
- node_a = graph.create_node(0)
53
- node_b = graph.create_node(1)
54
- edge = graph.create_edge(node_a, node_b)
55
- annotator = Seafoam::Annotators::FallbackAnnotator.new
56
- annotator.annotate graph
57
- expect(edge.props[:kind]).to eq 'other'
58
- end
59
-
60
- it 'does not overwrite an existing edge kind annotations' do
61
- graph = Seafoam::Graph.new
62
- node_a = graph.create_node(0)
63
- node_b = graph.create_node(1)
64
- edge = graph.create_edge(node_a, node_b, kind: 'control')
65
- annotator = Seafoam::Annotators::FallbackAnnotator.new
66
- annotator.annotate graph
67
- expect(edge.props[:kind]).to eq 'control'
68
- end
69
- end