Printing information about a bitstream's content is extremely useful, particularly
during development and debugging. Also, when two or more separate organizations are
developing a specification and pursuing independent implementations, it is very important
to have traces of the files generated by each tool for comparison and debugging purposes.
One could of course insert verbatim printf statements for all parsable
variables, but this can quickly get out of hand.
The translator can automatically generate tracing code and insert it in the get()
method (using the command line option -t, or the pragma directive trace). This means that, without
modifying your program or Flavor source in any way, you can automatically create detailed
traces of your files.
The tracing output is directed to the standard output. Note that the translator does
not print out the trace directly. Instead, it calls a quite simple trace
function which is part of the run-time library. This means that you can very easily use
your own tracing functions to customize both the output destination as well as formatting
(more information is provided in the Run-Time Library). In the
following we describe the trace output as provided by the standard run-time library
implementation.
Let's first define a complex representation that will excercise all of the tracing features.
class Base : int(2) id = 0 { |
The structure is as follows. Our Test class contains three objects of type
Base. The Base objects, in turn, our polymorphic; any class out
of Base, Derived1, or Derived2 can appear in their
place. The Base object is composed of two markers having fixed values,
followed by a length specification and a number of bytes equal to that length. The Derived1
and Derived2 objects each add a fixed variable, either an integer or a
double.
We created a Test object where each of the three Base objects
it contains is of type Base, Derived1, and Derived2
respectively, in that order. The data contained in variable data of each
class is the name of the class in ASCII. The following is trace produced by calling the Test.get()
method on that sample file.
|
As you can see, the trace output includes in each line: the bit position (starting from the beginning of the bitstream), the size of the quantity read, its value in hexadecimal (or floating point, if applicable), and a description. In some cases there is no length or value indication, meaning that no variable was parsed.
The first line indicates 'begin Test', signalling the entry in the get()
method of Test. For all classes, the beginning and end of the get()
method is signalled by 'begin' and 'end'. Then, we have the
message 'processing Base b[0]'. This message is always printed before
processing a variable of type class (i.e., before calling its own get()
method), so that it is clear which member of the original class is being parsed. As you
can see the 'processing Base b[0]' message is immediately followed by 'begin
Base', signalled by the entry to Base::get(). The trace then contains
each of the variables of Base, including their hexadecimal (3rd column) and actual decimal
value (in parentheses, in the description text).
Moving forward, at bit position 75 we have the beginning of Test.b[1]. As
this is actually a Derived1 object, the Derived1 get()
function is called; as a result, the trace indicates begin Derived1. Note
that the ID (id) is actually parsed by the derived class. Immediately
afterwards the get() method of the Base class is called. When it ends, the Derived1
get() continues to parse the d1 variable. The situation is
repeated for Test.b[2], starting at bit 190. Notice that in bit position 297
where the double variable d2 is traced, the 'Value' column
indicates the actual floating point value instead of the raw bits. The trace ends at the
end of the Test.get() method, which is signalled at bit 361 with the 'end
Test' message.
In this particular case no error was detected. Bitstream syntax errors occur when a
parsable variable has an expected value (e.g., the marker1 variable or the
object ID id). When such an error is detected the trace output will include
the string '(incorrect)' in the end of the description.
Also, a descriptive message is printed when the ID lookup for polymorphic classes
fails. For example, at bit 190, and after the 'processing Base b[1]' message,
if the ID didn't match any of Base or its derived classes the following
message would be printed: 'ID b[1].id lookup failed'. In order to allow the
code to continue, the base class type is used to create the new object. To demonstrate
error handling, we ran the program in an arbitrary binary file so that it gets complete
confused. Here is the output.
|
As we can see, almost all the markers are incorrect. The IDs, because of their short
length (2 bits), turn out to be correct except the one for b[2] at bit 334.
The generated code uses the base class Base in order to be able to continue.