seafoam 0.2 → 0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/workflows.yml +39 -0
  3. data/.rubocop.yml +4 -1
  4. data/README.md +107 -27
  5. data/bin/bgv2isabelle +16 -45
  6. data/bin/bgv2json +18 -36
  7. data/bin/cfg2asm +24 -0
  8. data/bin/seafoam +2 -2
  9. data/demos/box-unbox-stats +65 -0
  10. data/docs/bgv.md +16 -7
  11. data/docs/getting-graphs.md +12 -0
  12. data/docs/json.md +35 -0
  13. data/examples/fib-java.bgv.gz +0 -0
  14. data/examples/fib.js +1 -1
  15. data/examples/java/JavaExamples.java +1 -1
  16. data/examples/ruby/clamps.rb +20 -0
  17. data/examples/ruby/graal.patch +15 -0
  18. data/examples/ruby/ruby_examples.rb +278 -0
  19. data/lib/seafoam.rb +5 -1
  20. data/lib/seafoam/annotators/graal.rb +1 -1
  21. data/lib/seafoam/bgv/bgv_parser.rb +12 -2
  22. data/lib/seafoam/cfg/cfg_parser.rb +93 -0
  23. data/lib/seafoam/cfg/disassembler.rb +70 -0
  24. data/lib/seafoam/commands.rb +196 -36
  25. data/lib/seafoam/graal/source.rb +23 -0
  26. data/lib/seafoam/isabelle_writer.rb +46 -0
  27. data/lib/seafoam/json_writer.rb +58 -0
  28. data/lib/seafoam/version.rb +1 -1
  29. data/seafoam.gemspec +4 -2
  30. data/spec/seafoam/annotators/graal_spec.rb +7 -7
  31. data/spec/seafoam/bgv/bgv_parser_spec.rb +27 -14
  32. data/spec/seafoam/cfg/cfg_parser_spec.rb +21 -0
  33. data/spec/seafoam/cfg/disassembler_spec.rb +32 -0
  34. data/spec/seafoam/command_spec.rb +94 -30
  35. data/spec/seafoam/graphviz_writer_spec.rb +2 -2
  36. data/spec/seafoam/json_writer_spec.rb +14 -0
  37. data/spec/seafoam/spec_helpers.rb +5 -1
  38. data/spec/seafoam/spotlight_spec.rb +1 -1
  39. data/tools/render-all +2 -2
  40. metadata +34 -98
  41. data/.github/workflows/rubocop.yml +0 -10
  42. data/.github/workflows/specs.yml +0 -19
  43. data/examples/fib-java.bgv +0 -0
  44. data/examples/fib-js.bgv +0 -0
  45. data/examples/fib-ruby.bgv +0 -0
  46. data/examples/identity.bgv +0 -0
  47. data/examples/java/exampleArithOperator.bgv +0 -0
  48. data/examples/java/exampleArithOperator.cfg +0 -925
  49. data/examples/java/exampleArrayAllocation.bgv +0 -0
  50. data/examples/java/exampleArrayAllocation.cfg +0 -5268
  51. data/examples/java/exampleArrayRead.bgv +0 -0
  52. data/examples/java/exampleArrayRead.cfg +0 -2263
  53. data/examples/java/exampleArrayWrite.bgv +0 -0
  54. data/examples/java/exampleArrayWrite.cfg +0 -2315
  55. data/examples/java/exampleCatch.bgv +0 -0
  56. data/examples/java/exampleCatch.cfg +0 -4150
  57. data/examples/java/exampleCompareOperator.bgv +0 -0
  58. data/examples/java/exampleCompareOperator.cfg +0 -1109
  59. data/examples/java/exampleDoubleSynchronized.bgv +0 -0
  60. data/examples/java/exampleDoubleSynchronized.cfg +0 -26497
  61. data/examples/java/exampleExactArith.bgv +0 -0
  62. data/examples/java/exampleExactArith.cfg +0 -1888
  63. data/examples/java/exampleFieldRead.bgv +0 -0
  64. data/examples/java/exampleFieldRead.cfg +0 -1228
  65. data/examples/java/exampleFieldWrite.bgv +0 -0
  66. data/examples/java/exampleFieldWrite.cfg +0 -1102
  67. data/examples/java/exampleFor.bgv +0 -0
  68. data/examples/java/exampleFor.cfg +0 -3936
  69. data/examples/java/exampleFullEscape.bgv +0 -0
  70. data/examples/java/exampleFullEscape.cfg +0 -5893
  71. data/examples/java/exampleIf.bgv +0 -0
  72. data/examples/java/exampleIf.cfg +0 -2462
  73. data/examples/java/exampleIfNeverTaken.bgv +0 -0
  74. data/examples/java/exampleIfNeverTaken.cfg +0 -2476
  75. data/examples/java/exampleInstanceOfManyImpls.bgv +0 -0
  76. data/examples/java/exampleInstanceOfManyImpls.cfg +0 -6391
  77. data/examples/java/exampleInstanceOfOneImpl.bgv +0 -0
  78. data/examples/java/exampleInstanceOfOneImpl.cfg +0 -2604
  79. data/examples/java/exampleIntSwitch.bgv +0 -0
  80. data/examples/java/exampleIntSwitch.cfg +0 -3121
  81. data/examples/java/exampleInterfaceCallManyImpls.bgv +0 -0
  82. data/examples/java/exampleInterfaceCallManyImpls.cfg +0 -1358
  83. data/examples/java/exampleInterfaceCallOneImpl.bgv +0 -0
  84. data/examples/java/exampleInterfaceCallOneImpl.cfg +0 -3859
  85. data/examples/java/exampleLocalInstanceOf.bgv +0 -0
  86. data/examples/java/exampleLocalInstanceOf.cfg +0 -5276
  87. data/examples/java/exampleLocalSynchronized.bgv +0 -0
  88. data/examples/java/exampleLocalSynchronized.cfg +0 -1364
  89. data/examples/java/exampleLocalVariables.bgv +0 -0
  90. data/examples/java/exampleLocalVariables.cfg +0 -1195
  91. data/examples/java/exampleLocalVariablesState.bgv +0 -0
  92. data/examples/java/exampleLocalVariablesState.cfg +0 -1673
  93. data/examples/java/exampleNestedWhile.bgv +0 -0
  94. data/examples/java/exampleNestedWhile.cfg +0 -15499
  95. data/examples/java/exampleNestedWhileBreak.bgv +0 -0
  96. data/examples/java/exampleNestedWhileBreak.cfg +0 -11162
  97. data/examples/java/exampleNoEscape.bgv +0 -0
  98. data/examples/java/exampleNoEscape.cfg +0 -974
  99. data/examples/java/exampleObjectAllocation.bgv +0 -0
  100. data/examples/java/exampleObjectAllocation.cfg +0 -5287
  101. data/examples/java/examplePartialEscape.bgv +0 -0
  102. data/examples/java/examplePartialEscape.cfg +0 -7042
  103. data/examples/java/examplePhi.bgv +0 -0
  104. data/examples/java/examplePhi.cfg +0 -3227
  105. data/examples/java/exampleReducible.bgv +0 -0
  106. data/examples/java/exampleReducible.cfg +0 -5578
  107. data/examples/java/exampleSimpleCall.bgv +0 -0
  108. data/examples/java/exampleSimpleCall.cfg +0 -1435
  109. data/examples/java/exampleStamp.bgv +0 -0
  110. data/examples/java/exampleStamp.cfg +0 -913
  111. data/examples/java/exampleStaticCall.bgv +0 -0
  112. data/examples/java/exampleStaticCall.cfg +0 -1154
  113. data/examples/java/exampleStringSwitch.bgv +0 -0
  114. data/examples/java/exampleStringSwitch.cfg +0 -15377
  115. data/examples/java/exampleSynchronized.bgv +0 -0
  116. data/examples/java/exampleSynchronized.cfg +0 -26027
  117. data/examples/java/exampleThrow.bgv +0 -0
  118. data/examples/java/exampleThrow.cfg +0 -780
  119. data/examples/java/exampleThrowCatch.bgv +0 -0
  120. data/examples/java/exampleThrowCatch.cfg +0 -744
  121. data/examples/java/exampleUnsafeRead.bgv +0 -0
  122. data/examples/java/exampleUnsafeRead.cfg +0 -912
  123. data/examples/java/exampleUnsafeWrite.bgv +0 -0
  124. data/examples/java/exampleUnsafeWrite.cfg +0 -962
  125. data/examples/java/exampleWhile.bgv +0 -0
  126. data/examples/java/exampleWhile.cfg +0 -3936
  127. data/examples/java/exampleWhileBreak.bgv +0 -0
  128. data/examples/java/exampleWhileBreak.cfg +0 -5963
  129. data/examples/matmult-java.bgv +0 -0
  130. data/examples/matmult-ruby.bgv +0 -0
  131. data/examples/overflow.bgv +0 -0
  132. data/lib/seafoam/binary/binary_reader.rb +0 -21
  133. data/spec/seafoam/bgv/fixtures/not.bgv +0 -1
  134. data/spec/seafoam/bgv/fixtures/unsupported.bgv +0 -1
@@ -0,0 +1,23 @@
1
+ module Seafoam
2
+ module Graal
3
+ # Routines for understanding source positions in Graal.
4
+ module Source
5
+ def self.render(source_position)
6
+ lines = []
7
+ caller = source_position
8
+ while caller
9
+ method = caller[:method]
10
+ lines.push render_method(method)
11
+ caller = caller[:caller]
12
+ end
13
+ lines.join("\n")
14
+ end
15
+
16
+ def self.render_method(method)
17
+ declaring_class = method[:declaring_class]
18
+ name = method[:method_name]
19
+ "#{declaring_class}##{name}"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,46 @@
1
+ module Seafoam
2
+ # Write graphs in the Isabelle file format.
3
+ class IsabelleWriter
4
+ def initialize(out)
5
+ @out = out
6
+ end
7
+
8
+ def write(index, name, graph)
9
+ # definition eg_short_cut_or1 :: IRGraph where
10
+ # "eg_short_cut_or1 =
11
+ # (add_node 14 ReturnNode [13] []
12
+ # (add_node 13 PhiNode [10, 11, 12] []
13
+ # (add_node 12 (ConstantNode 0) [] []
14
+ # (add_node 11 (ConstantNode 42) [] []
15
+ # (add_node 10 MergeNode [7, 9] [14]
16
+ # (add_node 9 EndNode [] []
17
+ # (add_node 8 BeginNode [] [9]
18
+ # (add_node 7 EndNode [] []
19
+ # (add_node 6 BeginNode [] [7]
20
+ # (add_node 5 IfNode [3] [6, 8]
21
+ # (add_node 3 (ShortCircuitOrNode False False) [1, 2] []
22
+ # (add_node 2 (ParameterNode 1) [] []
23
+ # (add_node 1 (ParameterNode 0) [] []
24
+ # (add_node 0 StartNode [] [5]
25
+ # empty_graph))))))))))))))"
26
+
27
+ @out.puts "graph#{index} = # #{name}"
28
+
29
+ graph.nodes.each_value do |node|
30
+ node_class = node.props[:node_class][:node_class]
31
+ case node_class
32
+ when 'org.graalvm.compiler.nodes.ConstantNode'
33
+ desc = "(ConstantNode #{node.props['rawvalue']})"
34
+ when 'org.graalvm.compiler.nodes.ParameterNode'
35
+ desc = "(ParameterNode #{node.props['index']})"
36
+ else
37
+ desc = node_class.split('.').last
38
+ end
39
+ inputs = node.inputs.map(&:from).map(&:id)
40
+ outputs = node.outputs.map(&:to).map(&:id)
41
+ @out.puts " (add_node #{node.id} #{desc} #{inputs.inspect} #{outputs.inspect}"
42
+ end
43
+ @out.puts ' empty_graph' + (')' * graph.nodes.size)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,58 @@
1
+ require 'json'
2
+
3
+ module Seafoam
4
+ # Write files in a JSON format.
5
+ class JSONWriter
6
+ def initialize(out)
7
+ @out = out
8
+ end
9
+
10
+ def write(name, graph)
11
+ nodes = []
12
+ edges = []
13
+
14
+ graph.nodes.each_value do |node|
15
+ nodes.push(
16
+ id: node.id,
17
+ props: node.props
18
+ )
19
+
20
+ node.outputs.each do |edge|
21
+ edges.push(
22
+ from: edge.from.id,
23
+ to: edge.to.id,
24
+ props: edge.props
25
+ )
26
+ end
27
+ end
28
+
29
+ object = {
30
+ name: name,
31
+ props: graph.props,
32
+ nodes: nodes,
33
+ edges: edges
34
+ }
35
+
36
+ @out.puts JSON.pretty_generate(prepare_json(object))
37
+ end
38
+
39
+ private
40
+
41
+ def prepare_json(object)
42
+ case object
43
+ when Float
44
+ if object.nan?
45
+ '[NaN]'
46
+ else
47
+ object
48
+ end
49
+ when Array
50
+ object.map { |o| prepare_json(o) }
51
+ when Hash
52
+ object.transform_values { |v| prepare_json(v) }
53
+ else
54
+ object
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,5 +1,5 @@
1
1
  module Seafoam
2
2
  MAJOR_VERSION = 0
3
- MINOR_VERSION = 2
3
+ MINOR_VERSION = 7
4
4
  VERSION = "#{MAJOR_VERSION}.#{MINOR_VERSION}"
5
5
  end
data/seafoam.gemspec CHANGED
@@ -8,12 +8,14 @@ Gem::Specification.new do |spec|
8
8
  spec.homepage = 'https://github.com/Shopify/seafoam'
9
9
  spec.license = 'MIT'
10
10
 
11
- spec.files = `git ls-files`.split("\n")
11
+ spec.files = `git ls-files`.split("\n").reject { |f| f.end_with?('.bgv') || f.end_with?('.cfg') }
12
12
  spec.bindir = 'bin'
13
- spec.executables = %w[seafoam bgv2json bgv2isabelle]
13
+ spec.executables = %w[seafoam bgv2json bgv2isabelle cfg2asm]
14
14
 
15
15
  spec.required_ruby_version = '>= 2.5.8'
16
16
 
17
+ spec.add_dependency 'crabstone', '~> 4.0'
18
+
17
19
  spec.add_development_dependency 'benchmark-ips', '~> 2.7'
18
20
  spec.add_development_dependency 'rspec', '~> 3.8'
19
21
  spec.add_development_dependency 'rubocop', '~> 0.74'
@@ -20,7 +20,7 @@ describe Seafoam::Annotators::GraalAnnotator do
20
20
  describe 'when run' do
21
21
  describe 'without options' do
22
22
  before :all do
23
- @graph = Seafoam::SpecHelpers.example_graph('matmult-ruby', 2)
23
+ @graph = Seafoam::SpecHelpers.example_graph('matmult-ruby', 8)
24
24
  annotator = Seafoam::Annotators::GraalAnnotator.new({})
25
25
  annotator.annotate @graph
26
26
  end
@@ -37,14 +37,14 @@ describe Seafoam::Annotators::GraalAnnotator do
37
37
  expect(@graph.edges.any? { |e| e.props[:kind].nil? }).to be_falsey
38
38
  end
39
39
 
40
- it 'annotates negated GuardNodes with "Guard not, else ..."' do
41
- expect(@graph.nodes[561].props['negated']).to be true
42
- expect(@graph.nodes[561].props[:label]).to start_with 'Guard not, else'
40
+ it 'annotates not negated GuardNodes with "Guard, else ..."' do
41
+ expect(@graph.nodes[7101].props['negated']).to be false
42
+ expect(@graph.nodes[7101].props[:label]).to start_with 'Guard, else'
43
43
  end
44
44
 
45
- it 'annotates not negated GuardNodes with "Guard, else ..."' do
46
- expect(@graph.nodes[559].props['negated']).to be false
47
- expect(@graph.nodes[559].props[:label]).to start_with 'Guard, else'
45
+ it 'annotates negated GuardNodes with "Guard not, else ..."' do
46
+ expect(@graph.nodes[7102].props['negated']).to be true
47
+ expect(@graph.nodes[7102].props[:label]).to start_with 'Guard not, else'
48
48
  end
49
49
  end
50
50
 
@@ -11,7 +11,7 @@ describe Seafoam::BGV::BGVParser do
11
11
 
12
12
  it 'can read full files' do
13
13
  Seafoam::SpecHelpers::SAMPLE_BGV.each do |file|
14
- parser = Seafoam::BGV::BGVParser.new(File.new(file))
14
+ parser = Seafoam::BGV::BGVParser.new(file)
15
15
  parser.read_file_header
16
16
  parser.skip_document_props
17
17
  loop do
@@ -24,9 +24,22 @@ describe Seafoam::BGV::BGVParser do
24
24
  end
25
25
  end
26
26
 
27
+ it 'can read full gzipped files' do
28
+ parser = Seafoam::BGV::BGVParser.new(File.expand_path('../../../examples/fib-java.bgv.gz', __dir__))
29
+ parser.read_file_header
30
+ parser.skip_document_props
31
+ loop do
32
+ index, = parser.read_graph_preheader
33
+ break unless index
34
+
35
+ parser.read_graph_header
36
+ parser.read_graph
37
+ end
38
+ end
39
+
27
40
  it 'can skip full files' do
28
41
  Seafoam::SpecHelpers::SAMPLE_BGV.each do |file|
29
- parser = Seafoam::BGV::BGVParser.new(File.new(file))
42
+ parser = Seafoam::BGV::BGVParser.new(file)
30
43
  parser.read_file_header
31
44
  parser.skip_document_props
32
45
  loop do
@@ -41,7 +54,7 @@ describe Seafoam::BGV::BGVParser do
41
54
 
42
55
  it 'can alternate skipping and reading full files' do
43
56
  Seafoam::SpecHelpers::SAMPLE_BGV.each do |file|
44
- parser = Seafoam::BGV::BGVParser.new(File.new(file))
57
+ parser = Seafoam::BGV::BGVParser.new(file)
45
58
  parser.read_file_header
46
59
  parser.skip_document_props
47
60
  skip = false
@@ -63,39 +76,39 @@ describe Seafoam::BGV::BGVParser do
63
76
 
64
77
  describe '#read_file_header' do
65
78
  it 'produces a version' do
66
- parser = Seafoam::BGV::BGVParser.new(File.new(@fib_java_bgv))
67
- expect(parser.read_file_header).to eq [6, 1]
79
+ parser = Seafoam::BGV::BGVParser.new(@fib_java_bgv)
80
+ expect(parser.read_file_header).to eq [7, 0]
68
81
  end
69
82
 
70
83
  it 'raises an error for files which are not BGV' do
71
- parser = Seafoam::BGV::BGVParser.new(File.new(File.expand_path('fixtures/not.bgv', __dir__)))
84
+ parser = Seafoam::BGV::BGVParser.new(File.expand_path('fixtures/not.bgv', __dir__))
72
85
  expect { parser.read_file_header }.to raise_error(EncodingError)
73
86
  end
74
87
 
75
88
  it 'raises an error for files which are an unsupported version of BGV' do
76
- parser = Seafoam::BGV::BGVParser.new(File.new(File.expand_path('fixtures/unsupported.bgv', __dir__)))
89
+ parser = Seafoam::BGV::BGVParser.new(File.expand_path('fixtures/unsupported.bgv', __dir__))
77
90
  expect { parser.read_file_header }.to raise_error(NotImplementedError)
78
91
  end
79
92
 
80
93
  it 'does not raise an error for an unsupported version of BGV if version_check is disabled' do
81
- parser = Seafoam::BGV::BGVParser.new(File.new(File.expand_path('fixtures/unsupported.bgv', __dir__)))
94
+ parser = Seafoam::BGV::BGVParser.new(File.expand_path('fixtures/unsupported.bgv', __dir__))
82
95
  expect(parser.read_file_header(version_check: false)).to eq [7, 2]
83
96
  end
84
97
  end
85
98
 
86
99
  describe '#read_graph_preheader' do
87
100
  it 'produces an index and id' do
88
- parser = Seafoam::BGV::BGVParser.new(File.new(@fib_java_bgv))
101
+ parser = Seafoam::BGV::BGVParser.new(@fib_java_bgv)
89
102
  parser.read_file_header
90
103
  parser.skip_document_props
91
104
  expect(parser.read_graph_preheader).to eq [0, 0]
92
105
  end
93
106
 
94
107
  it 'returns nil for end of file' do
95
- parser = Seafoam::BGV::BGVParser.new(File.new(@fib_java_bgv))
108
+ parser = Seafoam::BGV::BGVParser.new(@fib_java_bgv)
96
109
  parser.read_file_header
97
110
  parser.skip_document_props
98
- 51.times do
111
+ 54.times do
99
112
  expect(parser.read_graph_preheader).to_not be_nil
100
113
  parser.skip_graph_header
101
114
  parser.skip_graph
@@ -104,7 +117,7 @@ describe Seafoam::BGV::BGVParser do
104
117
  end
105
118
 
106
119
  it 'returns unique indicies' do
107
- parser = Seafoam::BGV::BGVParser.new(File.new(@fib_java_bgv))
120
+ parser = Seafoam::BGV::BGVParser.new(@fib_java_bgv)
108
121
  parser.read_file_header
109
122
  parser.skip_document_props
110
123
  indicies = []
@@ -120,7 +133,7 @@ describe Seafoam::BGV::BGVParser do
120
133
 
121
134
  describe '#read_graph_header' do
122
135
  it 'produces an expected header' do
123
- parser = Seafoam::BGV::BGVParser.new(File.new(@fib_java_bgv))
136
+ parser = Seafoam::BGV::BGVParser.new(@fib_java_bgv)
124
137
  parser.read_file_header
125
138
  parser.skip_document_props
126
139
  parser.read_graph_preheader
@@ -131,7 +144,7 @@ describe Seafoam::BGV::BGVParser do
131
144
 
132
145
  describe '#read_graph' do
133
146
  it 'produces an expected graph' do
134
- parser = Seafoam::BGV::BGVParser.new(File.new(@fib_java_bgv))
147
+ parser = Seafoam::BGV::BGVParser.new(@fib_java_bgv)
135
148
  parser.read_file_header
136
149
  parser.skip_document_props
137
150
  parser.read_graph_preheader
@@ -0,0 +1,21 @@
1
+ require 'seafoam'
2
+
3
+ require 'rspec'
4
+
5
+ require_relative '../spec_helpers'
6
+
7
+ describe Seafoam::CFG::CFGParser do
8
+ before :all do
9
+ @example_cfg = File.expand_path('../../../examples/java/exampleIf.cfg', __dir__)
10
+ end
11
+
12
+ it 'correctly parses information from an nmethod' do
13
+ parser = Seafoam::CFG::CFGParser.new($stdout, @example_cfg)
14
+ parser.skip_over_cfg 'After code installation'
15
+ nmethod = parser.read_nmethod
16
+ expect(nmethod.code.arch).to eq 'AMD64'
17
+ expect(nmethod.code.arch_width).to eq '64'
18
+ expect(nmethod.code.base).to eq 0x1183037a0
19
+ expect(nmethod.comments.size).to eq 25
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ require 'seafoam'
2
+
3
+ require 'rspec'
4
+
5
+ require_relative '../spec_helpers'
6
+
7
+ describe Seafoam::CFG::Disassembler do
8
+ if Seafoam::SpecHelpers.dependencies_installed?
9
+ it 'can start Capstone Disassembler disassemble an nmethod with and without comments' do
10
+ @example_cfg = File.expand_path('../../../examples/java/exampleIf.cfg', __dir__)
11
+ @file = File.open('tempfile_disassembler_spec.txt', 'w')
12
+ parser = Seafoam::CFG::CFGParser.new(@file, @example_cfg)
13
+ parser.skip_over_cfg 'After code installation'
14
+ @nmethod = parser.read_nmethod
15
+
16
+ disassembler = Seafoam::CFG::Disassembler.new(@file)
17
+ disassembler.disassemble(@nmethod, 0)
18
+ @file.close
19
+
20
+ expect(`wc -l 'tempfile_disassembler_spec.txt'`.to_i).to eq 46
21
+
22
+ @file = File.open('tempfile_disassembler_spec.txt', 'w')
23
+ disassembler = Seafoam::CFG::Disassembler.new(@file)
24
+ disassembler.disassemble(@nmethod, 2)
25
+ @file.close
26
+
27
+ expect(`wc -l 'tempfile_disassembler_spec.txt'`.to_i).to eq 46
28
+
29
+ File.delete(@file)
30
+ end
31
+ end
32
+ end
@@ -8,6 +8,7 @@ require 'rspec'
8
8
  describe Seafoam::Commands do
9
9
  before :all do
10
10
  @fib_java = File.expand_path('../../examples/fib-java.bgv', __dir__)
11
+ @fib_ruby = File.expand_path('../../examples/fib-ruby.bgv', __dir__)
11
12
  end
12
13
 
13
14
  before :each do
@@ -19,7 +20,7 @@ describe Seafoam::Commands do
19
20
  it 'prints format and version' do
20
21
  @commands.send :info, @fib_java
21
22
  lines = @out.string.lines.map(&:rstrip)
22
- expect(lines.first).to eq 'BGV 6.1'
23
+ expect(lines.first).to eq 'BGV 7.0'
23
24
  end
24
25
 
25
26
  it 'does not work on a graph' do
@@ -39,11 +40,11 @@ describe Seafoam::Commands do
39
40
  "#{@fib_java}:4 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.CanonicalizerPhase"
40
41
  ]
41
42
  expect(lines.drop(lines.length - 5)).to eq [
42
- "#{@fib_java}:46 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.PropagateDeoptimizeProbabilityPhase",
43
- "#{@fib_java}:47 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.InsertMembarsPhase",
44
- "#{@fib_java}:48 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.schedule.SchedulePhase",
45
- "#{@fib_java}:49 2:Fib.fib(int)/After phase org.graalvm.compiler.core.phases.LowTier",
46
- "#{@fib_java}:50 2:Fib.fib(int)/After low tier"
43
+ "#{@fib_java}:49 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.DeadCodeEliminationPhase",
44
+ "#{@fib_java}:50 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.PropagateDeoptimizeProbabilityPhase",
45
+ "#{@fib_java}:51 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.schedule.SchedulePhase",
46
+ "#{@fib_java}:52 2:Fib.fib(int)/After phase org.graalvm.compiler.core.phases.LowTier",
47
+ "#{@fib_java}:53 2:Fib.fib(int)/After low tier"
47
48
  ]
48
49
  end
49
50
 
@@ -127,8 +128,8 @@ describe Seafoam::Commands do
127
128
 
128
129
  describe '#props' do
129
130
  it 'prints properties for a file' do
130
- @commands.send :props, @fib_java.to_s
131
- expect(@out.string.gsub(/\n\n/, "\n")).to eq "{\n}\n"
131
+ @commands.send :props, @fib_java
132
+ expect(@out.string.gsub(/\n\n/, "\n")).to eq "{\n \"vm.uuid\": \"28729\"\n}\n"
132
133
  end
133
134
 
134
135
  it 'prints properties for a graph' do
@@ -150,6 +151,28 @@ describe Seafoam::Commands do
150
151
  end
151
152
  end
152
153
 
154
+ describe '#source' do
155
+ it 'prints source information for a node' do
156
+ @commands.send :source, "#{@fib_ruby}:8:2443"
157
+ expect(@out.string).to eq <<~SOURCE
158
+ java.lang.Math#addExact
159
+ org.truffleruby.core.numeric.IntegerNodes$AddNode#add
160
+ org.truffleruby.core.numeric.IntegerNodesFactory$AddNodeFactory$AddNodeGen#executeAdd
161
+ org.truffleruby.core.inlined.InlinedAddNode#intAdd
162
+ org.truffleruby.core.inlined.InlinedAddNodeGen#execute
163
+ org.truffleruby.language.control.IfElseNode#execute
164
+ org.truffleruby.language.control.SequenceNode#execute
165
+ org.truffleruby.language.arguments.CheckArityNode#execute
166
+ org.truffleruby.language.control.SequenceNode#execute
167
+ org.truffleruby.language.methods.CatchForMethodNode#execute
168
+ org.truffleruby.language.methods.ExceptionTranslatingNode#execute
169
+ org.truffleruby.language.RubyRootNode#execute
170
+ org.graalvm.compiler.truffle.runtime.OptimizedCallTarget#executeRootNode
171
+ org.graalvm.compiler.truffle.runtime.OptimizedCallTarget#profiledPERoot
172
+ SOURCE
173
+ end
174
+ end
175
+
153
176
  describe '#render' do
154
177
  it 'does not work on a file' do
155
178
  expect { @commands.send :render, @fib_java }.to raise_error(ArgumentError)
@@ -161,32 +184,57 @@ describe Seafoam::Commands do
161
184
  end
162
185
  end
163
186
 
164
- it 'supports -o out.pdf' do
165
- @commands.send :render, "#{@fib_java}:0", '--out', 'out.pdf'
166
- expect(`file out.pdf`).to start_with 'out.pdf: PDF document'
167
- end
187
+ if Seafoam::SpecHelpers.dependencies_installed?
188
+ it 'supports -o out.pdf' do
189
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.pdf'
190
+ expect(`file out.pdf`).to start_with 'out.pdf: PDF document'
191
+ end
168
192
 
169
- it 'supports -o out.svg' do
170
- @commands.send :render, "#{@fib_java}:0", '--out', 'out.svg'
171
- expect(`file out.svg`).to start_with 'out.svg: SVG Scalable Vector Graphics image'
172
- end
193
+ it 'supports -o out.svg' do
194
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.svg'
195
+ expect(`file out.svg`).to start_with 'out.svg: SVG Scalable Vector Graphics image'
196
+ end
173
197
 
174
- it 'supports -o out.png' do
175
- @commands.send :render, "#{@fib_java}:0", '--out', 'out.png'
176
- expect(`file out.png`).to start_with 'out.png: PNG image data'
177
- end
198
+ it 'supports -o out.png' do
199
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.png'
200
+ expect(`file out.png`).to start_with 'out.png: PNG image data'
201
+ end
178
202
 
179
- it 'supports -o out.dot' do
180
- @commands.send :render, "#{@fib_java}:0", '--out', 'out.dot'
181
- expect(`file out.dot`).to start_with 'out.dot: ASCII text'
182
- end
203
+ it 'supports -o out.dot' do
204
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.dot'
205
+ expect(`file out.dot`).to start_with 'out.dot: ASCII text'
206
+ end
183
207
 
184
- it 'supports spotlighting nodes' do
185
- @commands.send :render, "#{@fib_java}:0", '--spotlight', '13'
208
+ it 'supports spotlighting nodes' do
209
+ @commands.send :render, "#{@fib_java}:0", '--spotlight', '13'
210
+ end
211
+
212
+ it 'does not work on a node' do
213
+ expect { @commands.send :render, "#{@fib_java}:0:13" }.to raise_error(ArgumentError)
214
+ end
215
+ else
216
+ it 'raises an exception if Graphviz is not installed' do
217
+ expect do
218
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.pdf'
219
+ end.to raise_error(RuntimeError, /Could not run Graphviz - is it installed?/)
220
+ end
186
221
  end
222
+ end
187
223
 
188
- it 'does not work on a node' do
189
- expect { @commands.send :render, "#{@fib_java}:0:13" }.to raise_error(ArgumentError)
224
+ describe '#cfg2asm' do
225
+ if Seafoam::SpecHelpers.dependencies_installed?
226
+ it 'prints format and version' do
227
+ @commands.cfg2asm(File.expand_path('../../examples/java/exampleWhile.cfg', __dir__), '--no-comments')
228
+ lines = @out.string.lines.map(&:rstrip)
229
+ expect(lines[1]).to include "0x117df59e0:\tnop dword ptr [rax + rax]"
230
+ expect(lines[-1]).to include "0x117df5a47:\thlt"
231
+ end
232
+ else
233
+ it 'raises an exception if Capstone is not installed' do
234
+ expect do
235
+ @commands.cfg2asm(File.expand_path('../../examples/java/exampleWhile.cfg', __dir__), '--no-comments')
236
+ end.to raise_error(RuntimeError, /Could not load Capstone - is it installed?/)
237
+ end
190
238
  end
191
239
  end
192
240
 
@@ -198,13 +246,13 @@ describe Seafoam::Commands do
198
246
 
199
247
  describe '#help' do
200
248
  it 'does not take any arguments' do
201
- expect { @commands.send :help, 'foo' }.to raise_error(ArgumentError)
249
+ expect { @commands.send :seafoam, '--help', 'foo' }.to raise_error(ArgumentError)
202
250
  end
203
251
  end
204
252
 
205
253
  describe '#version' do
206
254
  it 'does not take any arguments' do
207
- expect { @commands.send :help, 'foo' }.to raise_error(ArgumentError)
255
+ expect { @commands.send :version, 'foo' }.to raise_error(ArgumentError)
208
256
  end
209
257
  end
210
258
 
@@ -248,5 +296,21 @@ describe Seafoam::Commands do
248
296
  expect(node).to eq 12
249
297
  expect(edge).to eq 81
250
298
  end
299
+
300
+ it 'parses a BGV file name with periods and colons' do
301
+ file, graph, node, edge = @commands.send(:parse_name, 'TruffleHotSpotCompilation-13029[Truffle::ThreadOperations.detect_recursion_<split-3a5973bc>].bgv:4')
302
+ expect(file).to eq 'TruffleHotSpotCompilation-13029[Truffle::ThreadOperations.detect_recursion_<split-3a5973bc>].bgv'
303
+ expect(graph).to eq 4
304
+ expect(node).to be_nil
305
+ expect(edge).to be_nil
306
+ end
307
+
308
+ it 'parses a BGV.gz file name with periods and colons' do
309
+ file, graph, node, edge = @commands.send(:parse_name, 'TruffleHotSpotCompilation-13029[Truffle::ThreadOperations.detect_recursion_<split-3a5973bc>].bgv.gz:4')
310
+ expect(file).to eq 'TruffleHotSpotCompilation-13029[Truffle::ThreadOperations.detect_recursion_<split-3a5973bc>].bgv.gz'
311
+ expect(graph).to eq 4
312
+ expect(node).to be_nil
313
+ expect(edge).to be_nil
314
+ end
251
315
  end
252
316
  end