seafoam 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +7 -0
  2. data/.github/probots.yml +2 -0
  3. data/.github/workflows/rubocop.yml +10 -0
  4. data/.github/workflows/specs.yml +19 -0
  5. data/.gitignore +7 -0
  6. data/.rubocop.yml +34 -0
  7. data/.ruby-version +1 -0
  8. data/.seafoam/config +1 -0
  9. data/CODE_OF_CONDUCT.md +128 -0
  10. data/CONTRIBUTING.md +5 -0
  11. data/Gemfile +2 -0
  12. data/LICENSE.md +7 -0
  13. data/README.md +298 -0
  14. data/bin/bgv2isabelle +53 -0
  15. data/bin/bgv2json +42 -0
  16. data/bin/seafoam +24 -0
  17. data/docs/annotators.md +43 -0
  18. data/docs/bgv.md +284 -0
  19. data/docs/getting-graphs.md +47 -0
  20. data/examples/Fib.java +24 -0
  21. data/examples/MatMult.java +39 -0
  22. data/examples/fib-java.bgv +0 -0
  23. data/examples/fib-js.bgv +0 -0
  24. data/examples/fib-ruby.bgv +0 -0
  25. data/examples/fib.js +15 -0
  26. data/examples/fib.rb +15 -0
  27. data/examples/identity.bgv +0 -0
  28. data/examples/identity.rb +13 -0
  29. data/examples/java/Irreducible.j +35 -0
  30. data/examples/java/IrreducibleDecompiled.java +21 -0
  31. data/examples/java/JavaExamples.java +418 -0
  32. data/examples/java/exampleArithOperator.bgv +0 -0
  33. data/examples/java/exampleArithOperator.cfg +925 -0
  34. data/examples/java/exampleArrayAllocation.bgv +0 -0
  35. data/examples/java/exampleArrayAllocation.cfg +5268 -0
  36. data/examples/java/exampleArrayRead.bgv +0 -0
  37. data/examples/java/exampleArrayRead.cfg +2263 -0
  38. data/examples/java/exampleArrayWrite.bgv +0 -0
  39. data/examples/java/exampleArrayWrite.cfg +2315 -0
  40. data/examples/java/exampleCatch.bgv +0 -0
  41. data/examples/java/exampleCatch.cfg +4150 -0
  42. data/examples/java/exampleCompareOperator.bgv +0 -0
  43. data/examples/java/exampleCompareOperator.cfg +1109 -0
  44. data/examples/java/exampleDoubleSynchronized.bgv +0 -0
  45. data/examples/java/exampleDoubleSynchronized.cfg +26497 -0
  46. data/examples/java/exampleExactArith.bgv +0 -0
  47. data/examples/java/exampleExactArith.cfg +1888 -0
  48. data/examples/java/exampleFieldRead.bgv +0 -0
  49. data/examples/java/exampleFieldRead.cfg +1228 -0
  50. data/examples/java/exampleFieldWrite.bgv +0 -0
  51. data/examples/java/exampleFieldWrite.cfg +1102 -0
  52. data/examples/java/exampleFor.bgv +0 -0
  53. data/examples/java/exampleFor.cfg +3936 -0
  54. data/examples/java/exampleFullEscape.bgv +0 -0
  55. data/examples/java/exampleFullEscape.cfg +5893 -0
  56. data/examples/java/exampleIf.bgv +0 -0
  57. data/examples/java/exampleIf.cfg +2462 -0
  58. data/examples/java/exampleIfNeverTaken.bgv +0 -0
  59. data/examples/java/exampleIfNeverTaken.cfg +2476 -0
  60. data/examples/java/exampleInstanceOfManyImpls.bgv +0 -0
  61. data/examples/java/exampleInstanceOfManyImpls.cfg +6391 -0
  62. data/examples/java/exampleInstanceOfOneImpl.bgv +0 -0
  63. data/examples/java/exampleInstanceOfOneImpl.cfg +2604 -0
  64. data/examples/java/exampleIntSwitch.bgv +0 -0
  65. data/examples/java/exampleIntSwitch.cfg +3121 -0
  66. data/examples/java/exampleInterfaceCallManyImpls.bgv +0 -0
  67. data/examples/java/exampleInterfaceCallManyImpls.cfg +1358 -0
  68. data/examples/java/exampleInterfaceCallOneImpl.bgv +0 -0
  69. data/examples/java/exampleInterfaceCallOneImpl.cfg +3859 -0
  70. data/examples/java/exampleLocalInstanceOf.bgv +0 -0
  71. data/examples/java/exampleLocalInstanceOf.cfg +5276 -0
  72. data/examples/java/exampleLocalSynchronized.bgv +0 -0
  73. data/examples/java/exampleLocalSynchronized.cfg +1364 -0
  74. data/examples/java/exampleLocalVariables.bgv +0 -0
  75. data/examples/java/exampleLocalVariables.cfg +1195 -0
  76. data/examples/java/exampleLocalVariablesState.bgv +0 -0
  77. data/examples/java/exampleLocalVariablesState.cfg +1673 -0
  78. data/examples/java/exampleNestedWhile.bgv +0 -0
  79. data/examples/java/exampleNestedWhile.cfg +15499 -0
  80. data/examples/java/exampleNestedWhileBreak.bgv +0 -0
  81. data/examples/java/exampleNestedWhileBreak.cfg +11162 -0
  82. data/examples/java/exampleNoEscape.bgv +0 -0
  83. data/examples/java/exampleNoEscape.cfg +974 -0
  84. data/examples/java/exampleObjectAllocation.bgv +0 -0
  85. data/examples/java/exampleObjectAllocation.cfg +5287 -0
  86. data/examples/java/examplePartialEscape.bgv +0 -0
  87. data/examples/java/examplePartialEscape.cfg +7042 -0
  88. data/examples/java/examplePhi.bgv +0 -0
  89. data/examples/java/examplePhi.cfg +3227 -0
  90. data/examples/java/exampleReducible.bgv +0 -0
  91. data/examples/java/exampleReducible.cfg +5578 -0
  92. data/examples/java/exampleSimpleCall.bgv +0 -0
  93. data/examples/java/exampleSimpleCall.cfg +1435 -0
  94. data/examples/java/exampleStamp.bgv +0 -0
  95. data/examples/java/exampleStamp.cfg +913 -0
  96. data/examples/java/exampleStaticCall.bgv +0 -0
  97. data/examples/java/exampleStaticCall.cfg +1154 -0
  98. data/examples/java/exampleStringSwitch.bgv +0 -0
  99. data/examples/java/exampleStringSwitch.cfg +15377 -0
  100. data/examples/java/exampleSynchronized.bgv +0 -0
  101. data/examples/java/exampleSynchronized.cfg +26027 -0
  102. data/examples/java/exampleThrow.bgv +0 -0
  103. data/examples/java/exampleThrow.cfg +780 -0
  104. data/examples/java/exampleThrowCatch.bgv +0 -0
  105. data/examples/java/exampleThrowCatch.cfg +744 -0
  106. data/examples/java/exampleUnsafeRead.bgv +0 -0
  107. data/examples/java/exampleUnsafeRead.cfg +912 -0
  108. data/examples/java/exampleUnsafeWrite.bgv +0 -0
  109. data/examples/java/exampleUnsafeWrite.cfg +962 -0
  110. data/examples/java/exampleWhile.bgv +0 -0
  111. data/examples/java/exampleWhile.cfg +3936 -0
  112. data/examples/java/exampleWhileBreak.bgv +0 -0
  113. data/examples/java/exampleWhileBreak.cfg +5963 -0
  114. data/examples/matmult-java.bgv +0 -0
  115. data/examples/matmult-ruby.bgv +0 -0
  116. data/examples/matmult.rb +29 -0
  117. data/examples/overflow.bgv +0 -0
  118. data/examples/overflow.rb +13 -0
  119. data/lib/seafoam.rb +13 -0
  120. data/lib/seafoam/annotators.rb +54 -0
  121. data/lib/seafoam/annotators/fallback.rb +27 -0
  122. data/lib/seafoam/annotators/graal.rb +376 -0
  123. data/lib/seafoam/bgv/bgv_parser.rb +602 -0
  124. data/lib/seafoam/binary/binary_reader.rb +21 -0
  125. data/lib/seafoam/binary/io_binary_reader.rb +88 -0
  126. data/lib/seafoam/colors.rb +18 -0
  127. data/lib/seafoam/commands.rb +447 -0
  128. data/lib/seafoam/config.rb +34 -0
  129. data/lib/seafoam/graph.rb +91 -0
  130. data/lib/seafoam/graphviz_writer.rb +213 -0
  131. data/lib/seafoam/spotlight.rb +28 -0
  132. data/lib/seafoam/version.rb +5 -0
  133. data/seafoam.gemspec +20 -0
  134. data/spec/seafoam/annotators/fallback_spec.rb +69 -0
  135. data/spec/seafoam/annotators/graal_spec.rb +96 -0
  136. data/spec/seafoam/annotators_spec.rb +61 -0
  137. data/spec/seafoam/bgv/bgv_parser_spec.rb +144 -0
  138. data/spec/seafoam/bgv/fixtures/not.bgv +1 -0
  139. data/spec/seafoam/bgv/fixtures/unsupported.bgv +1 -0
  140. data/spec/seafoam/binary/io_binary_reader_spec.rb +176 -0
  141. data/spec/seafoam/command_spec.rb +252 -0
  142. data/spec/seafoam/graph_spec.rb +172 -0
  143. data/spec/seafoam/graphviz_writer_spec.rb +63 -0
  144. data/spec/seafoam/spec_helpers.rb +30 -0
  145. data/spec/seafoam/spotlight_spec.rb +38 -0
  146. data/tools/render-all +36 -0
  147. metadata +238 -0
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'seafoam'
4
+
5
+ # definition eg_short_cut_or1 :: IRGraph where
6
+ # "eg_short_cut_or1 =
7
+ # (add_node 14 ReturnNode [13] []
8
+ # (add_node 13 PhiNode [10, 11, 12] []
9
+ # (add_node 12 (ConstantNode 0) [] []
10
+ # (add_node 11 (ConstantNode 42) [] []
11
+ # (add_node 10 MergeNode [7, 9] [14]
12
+ # (add_node 9 EndNode [] []
13
+ # (add_node 8 BeginNode [] [9]
14
+ # (add_node 7 EndNode [] []
15
+ # (add_node 6 BeginNode [] [7]
16
+ # (add_node 5 IfNode [3] [6, 8]
17
+ # (add_node 3 (ShortCircuitOrNode False False) [1, 2] []
18
+ # (add_node 2 (ParameterNode 1) [] []
19
+ # (add_node 1 (ParameterNode 0) [] []
20
+ # (add_node 0 StartNode [] [5]
21
+ # empty_graph))))))))))))))"
22
+
23
+ ARGV.each do |file|
24
+ parser = Seafoam::BGV::BGVParser.new(File.new(file))
25
+ parser.read_file_header
26
+ parser.skip_document_props
27
+
28
+ loop do
29
+ index, = parser.read_graph_preheader
30
+ break unless index
31
+
32
+ graph_header = parser.read_graph_header
33
+ graph = parser.read_graph
34
+
35
+ puts "graph#{index} = # #{parser.graph_name(graph_header)}"
36
+
37
+ graph.nodes.each_value do |node|
38
+ node_class = node.props[:node_class][:node_class]
39
+ case node_class
40
+ when 'org.graalvm.compiler.nodes.ConstantNode'
41
+ desc = "(ConstantNode #{node.props['rawvalue']})"
42
+ when 'org.graalvm.compiler.nodes.ParameterNode'
43
+ desc = "(ParameterNode #{node.props['index']})"
44
+ else
45
+ desc = node_class.split('.').last
46
+ end
47
+ inputs = node.inputs.map(&:from).map(&:id)
48
+ outputs = node.outputs.map(&:to).map(&:id)
49
+ puts " (add_node #{node.id} #{desc} #{inputs.inspect} #{outputs.inspect}"
50
+ end
51
+ puts ' empty_graph' + (')' * graph.nodes.size)
52
+ end
53
+ end
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'seafoam'
4
+ require 'json'
5
+
6
+ ARGV.each do |file|
7
+ parser = Seafoam::BGV::BGVParser.new(File.new(file))
8
+ parser.read_file_header
9
+ parser.skip_document_props
10
+
11
+ loop do
12
+ index, = parser.read_graph_preheader
13
+ break unless index
14
+
15
+ graph_header = parser.read_graph_header
16
+
17
+ graph = parser.read_graph
18
+ Seafoam::Annotators.apply graph
19
+
20
+ name = parser.graph_name(graph_header)
21
+
22
+ nodes = []
23
+ edges = []
24
+
25
+ graph.nodes.each_value do |node|
26
+ nodes.push({
27
+ id: node.id,
28
+ props: node.props
29
+ })
30
+
31
+ node.outputs.each do |edge|
32
+ edges.push({
33
+ from: edge.from.id,
34
+ to: edge.to.id,
35
+ props: edge.props
36
+ })
37
+ end
38
+ end
39
+
40
+ puts JSON.generate({name: name, props: graph.props, nodes: nodes, edges: edges})
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'seafoam'
4
+
5
+ # This is the 'seafoam' command line entry point.
6
+
7
+ begin
8
+ # Load configuraiton.
9
+ config = Seafoam::Config.new
10
+ config.load_config
11
+
12
+ # Run the command line.
13
+ commands = Seafoam::Commands.new($stdout, config)
14
+ commands.run(*ARGV)
15
+ rescue StandardError => e
16
+ if $DEBUG
17
+ # Re-raise the exception so the user sees it, it debugging is
18
+ # enabled (ruby -d).
19
+ raise e
20
+ else
21
+ # Otherwise, just print the message.
22
+ warn "seafoam: #{e.message}"
23
+ end
24
+ end
@@ -0,0 +1,43 @@
1
+ # Annotators
2
+
3
+ The model of Seafoam is that it is a directed graph, with nodes annotated with a
4
+ bag of key-value properties. When graphs are read they'll have some initial
5
+ properties. Seafoam may add more properties. The output routines then draw the
6
+ graph using the graph structure itself and also the properties.
7
+
8
+ *Annotators* are routines that add extra properties to graphs to help the user
9
+ understand them. They usually have knowledge of how the graph is structured, and
10
+ use this to make the graph easier to read or to make important information in
11
+ them stand out.
12
+
13
+ We call properties with symbol keys *annotations*, as they're added by the
14
+ annotators.
15
+
16
+ Annotators are similar to filters in IGV.
17
+
18
+ Appropriate annotators are automatically selected and applied to graphs.
19
+
20
+ The rendering commands recognize these annotations, so annotators will probably
21
+ want to add them:
22
+
23
+ * `:label` on nodes and edges
24
+ * `:out_annotation` on nodes
25
+ * `:hidden` on nodes only, to not show them (*remove frame state* for example in IGV terminology)
26
+ * `:inlined` on nodes only, to indicate the node should be shown immediately above each node using it (*reduce edges* in IGV terminology)
27
+ * `:kind` on nodes only, which can be `info`, `input`, `control`, `effect`, `virtual`, `calc`, `guard`, or `other`
28
+ * `:kind` on edges only, which can be `info`, `control`, `loop`, or `data`
29
+ * `:reverse` on edges only
30
+ * `:spotlight` for nodes as part of spotlighting (`lit` are shown, `shaded` are shown but greyed out, and edges from `shaded` to `:hidden` nodes are also shown greyed out)
31
+
32
+ Seafoam ships with an annotator for generic GraalVM graphs. If you work with a
33
+ different compiler you'll probably want to write your own annotators - subclass
34
+ `Seafoam::Annotator` and implement `#annotate(graph)`. You can add custom
35
+ annotators by requiring them in your `.seafoam/config`. All subclasses of
36
+ `Annotator` will be found, so you just need to define the subclass.
37
+
38
+ A fallback annotator, run after all others, just labels the node with the
39
+ `'label'` property if there is one.
40
+
41
+ Options for annotations can be set on the command line with `--option key
42
+ value`. Some options are built into Seafoam commands, like `--show-frame-state`,
43
+ but they're doing the same thing as `--option hide_frame_state false`.
@@ -0,0 +1,284 @@
1
+ # BGV File Format
2
+
3
+ The BGV (*binary graph*, sometimes also called *BIGV*) file format is dumped by
4
+ Graal graphs.
5
+
6
+ The BGV parser was implemented from the [open-source writer code][graph-protocol]
7
+ in the GraalVM compiler.
8
+
9
+ [graph-protocol]: https://github.com/oracle/graal/blob/master/compiler/src/org.graalvm.graphio/src/org/graalvm/graphio/GraphProtocol.java
10
+
11
+ ## Format
12
+
13
+ Seafoam supports BGV versions:
14
+
15
+ * 7.0 (GraalVM 20.1.0 and up)
16
+ * 6.1 (GraalVM 1.0.0-rc16 and up)
17
+
18
+ The BGV file format is network-endian.
19
+
20
+ ```c
21
+ BGV {
22
+ char[4] = 'BIGV'
23
+ sint8 major
24
+ sint8 minor
25
+ GroupOrGraph*
26
+ }
27
+
28
+ GroupOrGraph {
29
+ BeginGroup GroupOrGraph* CloseGroup | Graph
30
+ }
31
+
32
+ BeginGroup {
33
+ sint8 token = BEGIN_GROUP
34
+ PoolObject name
35
+ PoolObject method
36
+ sint32 bci
37
+ Props props
38
+ }
39
+
40
+ Graph {
41
+ sint8 token = BEGIN_GRAPH
42
+ String format
43
+ Args args
44
+ GraphBody body
45
+ }
46
+
47
+ GraphBody {
48
+ Props props
49
+ sint32 nodes_count
50
+ Node[nodes_count] nodes
51
+ Blocks blocks
52
+ }
53
+
54
+ Node {
55
+ sint32 id
56
+ PoolObject node_class
57
+ bool has_predecessor
58
+ Pops props
59
+ Edge[node_class.inputs.size] edges_in
60
+ Edge[node_class.outputs.size] edges_out
61
+ }
62
+
63
+ Edge {
64
+ sint32[inputs_count] nodes
65
+ }
66
+
67
+ Blocks {
68
+ sint32 id
69
+ sint32 nodes_count
70
+ sint32[nodes_count] nodes
71
+ sint32 followers_count
72
+ sint32[followers_count] followers
73
+ }
74
+
75
+ CloseGroup {
76
+ sint8 token = CLOSE_GROUP
77
+ }
78
+
79
+ Props {
80
+ sint16 props_count
81
+ Prop[props_count] props
82
+ }
83
+
84
+ Prop {
85
+ PoolObject key
86
+ PropObject value
87
+ }
88
+
89
+ PropObject {
90
+ union {
91
+ struct {
92
+ sint8 PROPERTY_POOL
93
+ PoolObject object
94
+ }
95
+ struct {
96
+ sint8 PROPERTY_INT
97
+ sint32 value
98
+ }
99
+ struct {
100
+ sint8 PROPERTY_LONG
101
+ sint64 value
102
+ }
103
+ struct {
104
+ sint8 PROPERTY_DOUBLE
105
+ float64 value
106
+ }
107
+ struct {
108
+ sint8 PROPERTY_FLOAT
109
+ float32 value
110
+ }
111
+ struct {
112
+ sint8 PROPERTY_TRUE
113
+ }
114
+ struct {
115
+ sint8 PROPERTY_FALSE
116
+ }
117
+ struct {
118
+ sint8 PROPERTY_ARRAY
119
+ union {
120
+ struct {
121
+ sint8 PROPERTY_POOL
122
+ sint32 times
123
+ PoolObject[times] values
124
+ }
125
+ }
126
+ }
127
+ struct {
128
+ sint8 PROPERTY_SUBGRAPH
129
+ GraphBody graph
130
+ }
131
+ }
132
+ }
133
+
134
+ PoolObject {
135
+ union {
136
+ POOL_NULL
137
+ struct {
138
+ sint8 token = POOL_NEW
139
+ uint16 id
140
+ union {
141
+ struct {
142
+ sint8 type = POOL_STRING
143
+ String string
144
+ }
145
+ struct {
146
+ sint8 type = POOL_ENUM
147
+ PoolObject enum_class
148
+ sint32 enum_ordinal
149
+ }
150
+ struct {
151
+ sint8 type = POOL_CLASS
152
+ String type_name
153
+ union {
154
+ struct {
155
+ sint8 type = ENUM_KLASS
156
+ sint32 values_count
157
+ PoolObject values[values_count]
158
+ }
159
+ struct {
160
+ sint8 type = KLASS
161
+ }
162
+ }
163
+ }
164
+ struct {
165
+ sint8 type = POOL_METHOD
166
+ PoolObject declaring_class
167
+ PoolObject method_name
168
+ PoolObject signature
169
+ sint32 modifiers
170
+ sint32 bytes_length
171
+ uint8[bytes_length] bytes
172
+ }
173
+ struct {
174
+ sint8 type = POOL_NODE_CLASS
175
+ PoolObject node_class
176
+ String name_template
177
+ sint16 input_count
178
+ InputEdgeInfo inputs[input_count]
179
+ sint16 output_count
180
+ OutputEdgeInfo outputs[output_count]
181
+ }
182
+ struct {
183
+ sint8 type = POOL_FIELD
184
+ PoolObject field_class
185
+ PoolObject name
186
+ PoolObject type_name
187
+ sint32 modifiers
188
+ }
189
+ struct {
190
+ sint8 type = POOL_NODE_SIGNATURE
191
+ sint16 args_count
192
+ PoolObject args[args_count]
193
+ }
194
+ struct {
195
+ sint8 type = POOL_NODE_SOURCE_POSITION
196
+ PoolObject method
197
+ sint32 bci
198
+ SourcePosition source_positions[...until SourcePosition.uri = null]
199
+ PoolObject caller
200
+ }
201
+ struct {
202
+ sint8 type = POOL_NODE
203
+ sint32 node_id
204
+ PoolObject node_class
205
+ }
206
+ }
207
+ }
208
+ struct {
209
+ sint8 token = POOL_STRING | POOL_ENUM | POOL_CLASS | POOL_METHOD | POOL_NODE_CLASS | POOL_FIELD | POOL_SIGNATURE | POOL_NODE_SOURCE_POSITION | POOL_NODE
210
+ uint16 pool_id
211
+ }
212
+ }
213
+ }
214
+
215
+ InputEdgeInfo {
216
+ sint8 indirect
217
+ PoolObject name
218
+ PoolObject type
219
+ }
220
+
221
+ OutputEdgeInfo {
222
+ sint8 indirect
223
+ PoolObject name
224
+ }
225
+
226
+ SourcePosition {
227
+ PoolObject uri
228
+ String location
229
+ sint32 line
230
+ sint32 start
231
+ sint32 end
232
+ }
233
+
234
+ String {
235
+ sint32 length
236
+ char[length] chars
237
+ }
238
+
239
+ BEGIN_GROUP = 0x00
240
+ BEGIN_GRAPH = 0x01
241
+ CLOSE_GROUP = 0x02
242
+ BEGIN_DOCUMENT = 0x03
243
+
244
+ POOL_NEW = 0x00
245
+ POOL_STRING = 0x01
246
+ POOL_ENUM = 0x02
247
+ POOL_CLASS = 0x03
248
+ POOL_METHOD = 0x04
249
+ POOL_NULL = 0x05
250
+ POOL_NODE_CLASS = 0x06
251
+ POOL_FIELD = 0x07
252
+ POOL_SIGNATURE = 0x08
253
+ POOL_NODE_SOURCE_POSITION = 0x09
254
+ POOL_NODE = 0x0a
255
+
256
+ PROPERTY_POOL = 0x00
257
+ PROPERTY_INT = 0x01
258
+ PROPERTY_LONG = 0x02
259
+ PROPERTY_DOUBLE = 0x03
260
+ PROPERTY_FLOAT = 0x04
261
+ PROPERTY_TRUE = 0x05
262
+ PROPERTY_FALSE = 0x06
263
+ PROPERTY_ARRAY = 0x07
264
+ PROPERTY_SUBGRAPH = 0x08
265
+
266
+ KLASS = 0x00
267
+ ENUM_KLASS = 0x01
268
+ ```
269
+
270
+ ## Seeking
271
+
272
+ The BGV format is not particularly amenable to seeking. You must read each prior
273
+ graph in order to read any subsequent graph, the length of nodes and edges
274
+ depends on values within them, and there is an object pool that is incrementally
275
+ built.
276
+
277
+ In order to 'seek' BGV files we have two code paths for reading the file, one
278
+ with loads the data and one which loads as little as possible to know just how
279
+ much further to advance in the file.
280
+
281
+ ## Other information
282
+
283
+ `seafoam file.bgv debug` checks that a file can be parsed and prints information
284
+ as it does so.