seafoam 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/workflows.yml +39 -0
  3. data/.rubocop.yml +1 -1
  4. data/README.md +75 -2
  5. data/bin/bgv2isabelle +16 -45
  6. data/bin/bgv2json +18 -36
  7. data/bin/cfg2asm +24 -0
  8. data/bin/seafoam +1 -1
  9. data/demos/box-unbox-stats +65 -0
  10. data/docs/getting-graphs.md +4 -0
  11. data/examples/fib-java.bgv.gz +0 -0
  12. data/examples/fib.js +1 -1
  13. data/examples/java/JavaExamples.java +1 -1
  14. data/examples/ruby/clamps.rb +20 -0
  15. data/examples/ruby/graal.patch +15 -0
  16. data/examples/ruby/ruby_examples.rb +278 -0
  17. data/lib/seafoam.rb +5 -0
  18. data/lib/seafoam/annotators/graal.rb +1 -1
  19. data/lib/seafoam/cfg/cfg_parser.rb +93 -0
  20. data/lib/seafoam/cfg/disassembler.rb +70 -0
  21. data/lib/seafoam/commands.rb +185 -29
  22. data/lib/seafoam/graal/source.rb +23 -0
  23. data/lib/seafoam/isabelle_writer.rb +46 -0
  24. data/lib/seafoam/json_writer.rb +58 -0
  25. data/lib/seafoam/version.rb +1 -1
  26. data/seafoam.gemspec +2 -1
  27. data/spec/seafoam/annotators/graal_spec.rb +7 -7
  28. data/spec/seafoam/bgv/bgv_parser_spec.rb +2 -2
  29. data/spec/seafoam/cfg/cfg_parser_spec.rb +21 -0
  30. data/spec/seafoam/cfg/disassembler_spec.rb +32 -0
  31. data/spec/seafoam/command_spec.rb +78 -30
  32. data/spec/seafoam/json_writer_spec.rb +14 -0
  33. data/spec/seafoam/spec_helpers.rb +4 -0
  34. data/tools/render-all +2 -2
  35. metadata +36 -102
  36. data/.github/workflows/rubocop.yml +0 -10
  37. data/.github/workflows/specs.yml +0 -19
  38. data/examples/fib-java.bgv +0 -0
  39. data/examples/fib-js.bgv +0 -0
  40. data/examples/fib-ruby.bgv +0 -0
  41. data/examples/identity.bgv +0 -0
  42. data/examples/java/exampleArithOperator.bgv +0 -0
  43. data/examples/java/exampleArithOperator.cfg +0 -925
  44. data/examples/java/exampleArrayAllocation.bgv +0 -0
  45. data/examples/java/exampleArrayAllocation.cfg +0 -5268
  46. data/examples/java/exampleArrayRead.bgv +0 -0
  47. data/examples/java/exampleArrayRead.cfg +0 -2263
  48. data/examples/java/exampleArrayWrite.bgv +0 -0
  49. data/examples/java/exampleArrayWrite.cfg +0 -2315
  50. data/examples/java/exampleCatch.bgv +0 -0
  51. data/examples/java/exampleCatch.cfg +0 -4150
  52. data/examples/java/exampleCompareOperator.bgv +0 -0
  53. data/examples/java/exampleCompareOperator.cfg +0 -1109
  54. data/examples/java/exampleDoubleSynchronized.bgv +0 -0
  55. data/examples/java/exampleDoubleSynchronized.cfg +0 -26497
  56. data/examples/java/exampleExactArith.bgv +0 -0
  57. data/examples/java/exampleExactArith.cfg +0 -1888
  58. data/examples/java/exampleFieldRead.bgv +0 -0
  59. data/examples/java/exampleFieldRead.cfg +0 -1228
  60. data/examples/java/exampleFieldWrite.bgv +0 -0
  61. data/examples/java/exampleFieldWrite.cfg +0 -1102
  62. data/examples/java/exampleFor.bgv +0 -0
  63. data/examples/java/exampleFor.cfg +0 -3936
  64. data/examples/java/exampleFullEscape.bgv +0 -0
  65. data/examples/java/exampleFullEscape.cfg +0 -5893
  66. data/examples/java/exampleIf.bgv +0 -0
  67. data/examples/java/exampleIf.cfg +0 -2462
  68. data/examples/java/exampleIfNeverTaken.bgv +0 -0
  69. data/examples/java/exampleIfNeverTaken.cfg +0 -2476
  70. data/examples/java/exampleInstanceOfManyImpls.bgv +0 -0
  71. data/examples/java/exampleInstanceOfManyImpls.cfg +0 -6391
  72. data/examples/java/exampleInstanceOfOneImpl.bgv +0 -0
  73. data/examples/java/exampleInstanceOfOneImpl.cfg +0 -2604
  74. data/examples/java/exampleIntSwitch.bgv +0 -0
  75. data/examples/java/exampleIntSwitch.cfg +0 -3121
  76. data/examples/java/exampleInterfaceCallManyImpls.bgv +0 -0
  77. data/examples/java/exampleInterfaceCallManyImpls.cfg +0 -1358
  78. data/examples/java/exampleInterfaceCallOneImpl.bgv +0 -0
  79. data/examples/java/exampleInterfaceCallOneImpl.cfg +0 -3859
  80. data/examples/java/exampleLocalInstanceOf.bgv +0 -0
  81. data/examples/java/exampleLocalInstanceOf.cfg +0 -5276
  82. data/examples/java/exampleLocalSynchronized.bgv +0 -0
  83. data/examples/java/exampleLocalSynchronized.cfg +0 -1364
  84. data/examples/java/exampleLocalVariables.bgv +0 -0
  85. data/examples/java/exampleLocalVariables.cfg +0 -1195
  86. data/examples/java/exampleLocalVariablesState.bgv +0 -0
  87. data/examples/java/exampleLocalVariablesState.cfg +0 -1673
  88. data/examples/java/exampleNestedWhile.bgv +0 -0
  89. data/examples/java/exampleNestedWhile.cfg +0 -15499
  90. data/examples/java/exampleNestedWhileBreak.bgv +0 -0
  91. data/examples/java/exampleNestedWhileBreak.cfg +0 -11162
  92. data/examples/java/exampleNoEscape.bgv +0 -0
  93. data/examples/java/exampleNoEscape.cfg +0 -974
  94. data/examples/java/exampleObjectAllocation.bgv +0 -0
  95. data/examples/java/exampleObjectAllocation.cfg +0 -5287
  96. data/examples/java/examplePartialEscape.bgv +0 -0
  97. data/examples/java/examplePartialEscape.cfg +0 -7042
  98. data/examples/java/examplePhi.bgv +0 -0
  99. data/examples/java/examplePhi.cfg +0 -3227
  100. data/examples/java/exampleReducible.bgv +0 -0
  101. data/examples/java/exampleReducible.cfg +0 -5578
  102. data/examples/java/exampleSimpleCall.bgv +0 -0
  103. data/examples/java/exampleSimpleCall.cfg +0 -1435
  104. data/examples/java/exampleStamp.bgv +0 -0
  105. data/examples/java/exampleStamp.cfg +0 -913
  106. data/examples/java/exampleStaticCall.bgv +0 -0
  107. data/examples/java/exampleStaticCall.cfg +0 -1154
  108. data/examples/java/exampleStringSwitch.bgv +0 -0
  109. data/examples/java/exampleStringSwitch.cfg +0 -15377
  110. data/examples/java/exampleSynchronized.bgv +0 -0
  111. data/examples/java/exampleSynchronized.cfg +0 -26027
  112. data/examples/java/exampleThrow.bgv +0 -0
  113. data/examples/java/exampleThrow.cfg +0 -780
  114. data/examples/java/exampleThrowCatch.bgv +0 -0
  115. data/examples/java/exampleThrowCatch.cfg +0 -744
  116. data/examples/java/exampleUnsafeRead.bgv +0 -0
  117. data/examples/java/exampleUnsafeRead.cfg +0 -912
  118. data/examples/java/exampleUnsafeWrite.bgv +0 -0
  119. data/examples/java/exampleUnsafeWrite.cfg +0 -962
  120. data/examples/java/exampleWhile.bgv +0 -0
  121. data/examples/java/exampleWhile.cfg +0 -3936
  122. data/examples/java/exampleWhileBreak.bgv +0 -0
  123. data/examples/java/exampleWhileBreak.cfg +0 -5963
  124. data/examples/matmult-java.bgv +0 -0
  125. data/examples/matmult-ruby.bgv +0 -0
  126. data/examples/overflow.bgv +0 -0
  127. data/spec/seafoam/bgv/fixtures/not.bgv +0 -1
  128. data/spec/seafoam/bgv/fixtures/unsupported.bgv +0 -1
@@ -0,0 +1,15 @@
1
+ diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/inlining/DefaultInliningPolicy.java b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/inlining/DefaultInliningPolicy.java
2
+ index ffe01c1688f..d662a7096c4 100644
3
+ --- a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/inlining/DefaultInliningPolicy.java
4
+ +++ b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/inlining/DefaultInliningPolicy.java
5
+ @@ -114,6 +114,10 @@ final class DefaultInliningPolicy implements InliningPolicy {
6
+ final PriorityQueue<CallNode> inlineQueue = getQueue(tree, CallNode.State.Expanded);
7
+ CallNode candidate;
8
+ while ((candidate = inlineQueue.poll()) != null) {
9
+ + if (candidate.getName().startsWith("Object#opaque_") || candidate.getName().equals("Object#static_call") || candidate.getName().equals("ExampleObject#instance_call")) {
10
+ + continue;
11
+ + }
12
+ +
13
+ if (candidate.isTrivial()) {
14
+ candidate.inline();
15
+ continue;
@@ -0,0 +1,278 @@
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
data/lib/seafoam.rb CHANGED
@@ -1,12 +1,17 @@
1
1
  require 'seafoam/version'
2
2
  require 'seafoam/binary/io_binary_reader'
3
3
  require 'seafoam/bgv/bgv_parser'
4
+ require 'seafoam/cfg/cfg_parser'
5
+ require 'seafoam/cfg/disassembler'
4
6
  require 'seafoam/colors'
5
7
  require 'seafoam/graph'
8
+ require 'seafoam/graal/source'
6
9
  require 'seafoam/annotators'
7
10
  require 'seafoam/annotators/graal'
8
11
  require 'seafoam/annotators/fallback'
9
12
  require 'seafoam/spotlight'
13
+ require 'seafoam/isabelle_writer'
14
+ require 'seafoam/json_writer'
10
15
  require 'seafoam/graphviz_writer'
11
16
  require 'seafoam/config'
12
17
  require 'seafoam/commands'
@@ -357,7 +357,7 @@ module Seafoam
357
357
  TRIGGERS = %w[
358
358
  HostedGraphBuilderPhase
359
359
  GraalCompiler
360
- TruffleCompilerThread
360
+ TruffleCompiler
361
361
  ]
362
362
 
363
363
  # Simple input node classes that may be inlined.
@@ -0,0 +1,93 @@
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
@@ -0,0 +1,70 @@
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