]> git.cworth.org Git - apitrace/blob - common/trace_parser.cpp
Use skiplist-based FastCallSet within trace::CallSet
[apitrace] / common / trace_parser.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * Copyright 2010 VMware, Inc.
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
27
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "trace_file.hpp"
33 #include "trace_dump.hpp"
34 #include "trace_parser.hpp"
35
36
37 #define TRACE_VERBOSE 0
38
39
40 namespace trace {
41
42
43 Parser::Parser() {
44     file = NULL;
45     next_call_no = 0;
46     version = 0;
47     api = API_UNKNOWN;
48
49     glGetErrorSig = NULL;
50 }
51
52
53 Parser::~Parser() {
54     close();
55 }
56
57
58 bool Parser::open(const char *filename) {
59     assert(!file);
60     file = File::createForRead(filename);
61     if (!file) {
62         return false;
63     }
64
65     version = read_uint();
66     if (version > TRACE_VERSION) {
67         std::cerr << "error: unsupported trace format version " << version << "\n";
68         return false;
69     }
70     api = API_UNKNOWN;
71
72     return true;
73 }
74
75 template <typename Iter>
76 inline void
77 deleteAll(Iter begin, Iter end)
78 {
79     while (begin != end) {
80         delete *begin;
81         ++begin;
82     }
83 }
84
85 template <typename Container>
86 inline void
87 deleteAll(Container &c)
88 {
89     deleteAll(c.begin(), c.end());
90     c.clear();
91 }
92
93 void Parser::close(void) {
94     if (file) {
95         file->close();
96         delete file;
97         file = NULL;
98     }
99
100     deleteAll(calls);
101
102     // Delete all signature data.  Signatures are mere structures which don't
103     // own their own memory, so we need to destroy all data we created here.
104
105     for (FunctionMap::iterator it = functions.begin(); it != functions.end(); ++it) {
106         FunctionSigState *sig = *it;
107         if (sig) {
108             delete [] sig->name;
109             for (unsigned arg = 0; arg < sig->num_args; ++arg) {
110                 delete [] sig->arg_names[arg];
111             }
112             delete [] sig->arg_names;
113             delete sig;
114         }
115     }
116     functions.clear();
117
118     for (StructMap::iterator it = structs.begin(); it != structs.end(); ++it) {
119         StructSigState *sig = *it;
120         if (sig) {
121             delete [] sig->name;
122             for (unsigned member = 0; member < sig->num_members; ++member) {
123                 delete [] sig->member_names[member];
124             }
125             delete [] sig->member_names;
126             delete sig;
127         }
128     }
129     structs.clear();
130
131     for (EnumMap::iterator it = enums.begin(); it != enums.end(); ++it) {
132         EnumSigState *sig = *it;
133         if (sig) {
134             for (unsigned value = 0; value < sig->num_values; ++value) {
135                 delete [] sig->values[value].name;
136             }
137             delete [] sig->values;
138             delete sig;
139         }
140     }
141     enums.clear();
142     
143     for (BitmaskMap::iterator it = bitmasks.begin(); it != bitmasks.end(); ++it) {
144         BitmaskSigState *sig = *it;
145         if (sig) {
146             for (unsigned flag = 0; flag < sig->num_flags; ++flag) {
147                 delete [] sig->flags[flag].name;
148             }
149             delete [] sig->flags;
150             delete sig;
151         }
152     }
153     bitmasks.clear();
154
155     next_call_no = 0;
156 }
157
158
159 void Parser::getBookmark(ParseBookmark &bookmark) {
160     bookmark.offset = file->currentOffset();
161     bookmark.next_call_no = next_call_no;
162 }
163
164
165 void Parser::setBookmark(const ParseBookmark &bookmark) {
166     file->setCurrentOffset(bookmark.offset);
167     next_call_no = bookmark.next_call_no;
168     
169     // Simply ignore all pending calls
170     deleteAll(calls);
171 }
172
173
174 Call *Parser::parse_call(Mode mode) {
175     do {
176         Call *call;
177         int c = read_byte();
178         switch (c) {
179         case trace::EVENT_ENTER:
180 #if TRACE_VERBOSE
181             std::cerr << "\tENTER\n";
182 #endif
183             parse_enter(mode);
184             break;
185         case trace::EVENT_LEAVE:
186 #if TRACE_VERBOSE
187             std::cerr << "\tLEAVE\n";
188 #endif
189             call = parse_leave(mode);
190             if (call) {
191                 adjust_call_flags(call);
192                 return call;
193             }
194             break;
195         default:
196             std::cerr << "error: unknown event " << c << "\n";
197             exit(1);
198         case -1:
199             if (!calls.empty()) {
200                 call = calls.front();
201                 call->flags |= CALL_FLAG_INCOMPLETE;
202                 calls.pop_front();
203                 adjust_call_flags(call);
204                 return call;
205             }
206             return NULL;
207         }
208     } while(true);
209 }
210
211
212 /**
213  * Helper function to lookup an ID in a vector, resizing the vector if it doesn't fit.
214  */
215 template<class T>
216 T *lookup(std::vector<T *> &map, size_t index) {
217     if (index >= map.size()) {
218         map.resize(index + 1);
219         return NULL;
220     } else {
221         return map[index];
222     }
223 }
224
225
226 Parser::FunctionSigFlags *
227 Parser::parse_function_sig(void) {
228     size_t id = read_uint();
229
230     FunctionSigState *sig = lookup(functions, id);
231
232     if (!sig) {
233         /* parse the signature */
234         sig = new FunctionSigState;
235         sig->id = id;
236         sig->name = read_string();
237         sig->num_args = read_uint();
238         const char **arg_names = new const char *[sig->num_args];
239         for (unsigned i = 0; i < sig->num_args; ++i) {
240             arg_names[i] = read_string();
241         }
242         sig->arg_names = arg_names;
243         sig->flags = lookupCallFlags(sig->name);
244         sig->offset = file->currentOffset();
245         functions[id] = sig;
246
247         /**
248          * Try to autodetect the API.
249          *
250          * XXX: Ideally we would allow to mix multiple APIs in a single trace,
251          * but as it stands today, retrace is done separately for each API.
252          */
253         if (api == API_UNKNOWN) {
254             const char *n = sig->name;
255             if ((n[0] == 'g' && n[1] == 'l' && n[2] == 'X') || // glX*
256                 (n[0] == 'w' && n[1] == 'g' && n[2] == 'l' && n[3] >= 'A' && n[3] <= 'Z') || // wgl[A-Z]*
257                 (n[0] == 'C' && n[1] == 'G' && n[2] == 'L')) { // CGL*
258                 api = trace::API_GL;
259             } else if (n[0] == 'e' && n[1] == 'g' && n[2] == 'l' && n[3] >= 'A' && n[3] <= 'Z') { // egl[A-Z]*
260                 api = trace::API_EGL;
261             } else if ((n[0] == 'D' &&
262                         ((n[1] == 'i' && n[2] == 'r' && n[3] == 'e' && n[4] == 'c' && n[5] == 't') || // Direct*
263                          (n[1] == '3' && n[2] == 'D'))) || // D3D*
264                        (n[0] == 'C' && n[1] == 'r' && n[2] == 'e' && n[3] == 'a' && n[4] == 't' && n[5] == 'e')) { // Create*
265                 api = trace::API_DX;
266             } else {
267                 /* TODO */
268             }
269         }
270
271         /**
272          * Note down the signature of special functions for future reference.
273          *
274          * NOTE: If the number of comparisons increases we should move this to a
275          * separate function and use bisection.
276          */
277         if (sig->num_args == 0 &&
278             strcmp(sig->name, "glGetError") == 0) {
279             glGetErrorSig = sig;
280         }
281
282     } else if (file->currentOffset() < sig->offset) {
283         /* skip over the signature */
284         skip_string(); /* name */
285         unsigned num_args = read_uint();
286         for (unsigned i = 0; i < num_args; ++i) {
287              skip_string(); /*arg_name*/
288         }
289     }
290
291     assert(sig);
292     return sig;
293 }
294
295
296 StructSig *Parser::parse_struct_sig() {
297     size_t id = read_uint();
298
299     StructSigState *sig = lookup(structs, id);
300
301     if (!sig) {
302         /* parse the signature */
303         sig = new StructSigState;
304         sig->id = id;
305         sig->name = read_string();
306         sig->num_members = read_uint();
307         const char **member_names = new const char *[sig->num_members];
308         for (unsigned i = 0; i < sig->num_members; ++i) {
309             member_names[i] = read_string();
310         }
311         sig->member_names = member_names;
312         sig->offset = file->currentOffset();
313         structs[id] = sig;
314     } else if (file->currentOffset() < sig->offset) {
315         /* skip over the signature */
316         skip_string(); /* name */
317         unsigned num_members = read_uint();
318         for (unsigned i = 0; i < num_members; ++i) {
319             skip_string(); /* member_name */
320         }
321     }
322
323     assert(sig);
324     return sig;
325 }
326
327
328 /*
329  * Old enum signatures would cover a single name/value only:
330  *
331  *   enum_sig = id name value
332  *            | id
333  */
334 EnumSig *Parser::parse_old_enum_sig() {
335     size_t id = read_uint();
336
337     EnumSigState *sig = lookup(enums, id);
338
339     if (!sig) {
340         /* parse the signature */
341         sig = new EnumSigState;
342         sig->id = id;
343         sig->num_values = 1;
344         EnumValue *values = new EnumValue[sig->num_values];
345         values->name = read_string();
346         values->value = read_sint();
347         sig->values = values;
348         sig->offset = file->currentOffset();
349         enums[id] = sig;
350     } else if (file->currentOffset() < sig->offset) {
351         /* skip over the signature */
352         skip_string(); /*name*/
353         scan_value();
354     }
355
356     assert(sig);
357     return sig;
358 }
359
360
361 EnumSig *Parser::parse_enum_sig() {
362     size_t id = read_uint();
363
364     EnumSigState *sig = lookup(enums, id);
365
366     if (!sig) {
367         /* parse the signature */
368         sig = new EnumSigState;
369         sig->id = id;
370         sig->num_values = read_uint();
371         EnumValue *values = new EnumValue[sig->num_values];
372         for (EnumValue *it = values; it != values + sig->num_values; ++it) {
373             it->name = read_string();
374             it->value = read_sint();
375         }
376         sig->values = values;
377         sig->offset = file->currentOffset();
378         enums[id] = sig;
379     } else if (file->currentOffset() < sig->offset) {
380         /* skip over the signature */
381         int num_values = read_uint();
382         for (int i = 0; i < num_values; ++i) {
383             skip_string(); /*name */
384             skip_sint(); /* value */
385         }
386     }
387
388     assert(sig);
389     return sig;
390 }
391
392
393 BitmaskSig *Parser::parse_bitmask_sig() {
394     size_t id = read_uint();
395
396     BitmaskSigState *sig = lookup(bitmasks, id);
397
398     if (!sig) {
399         /* parse the signature */
400         sig = new BitmaskSigState;
401         sig->id = id;
402         sig->num_flags = read_uint();
403         BitmaskFlag *flags = new BitmaskFlag[sig->num_flags];
404         for (BitmaskFlag *it = flags; it != flags + sig->num_flags; ++it) {
405             it->name = read_string();
406             it->value = read_uint();
407             if (it->value == 0 && it != flags) {
408                 std::cerr << "warning: bitmask " << it->name << " is zero but is not first flag\n";
409             }
410         }
411         sig->flags = flags;
412         sig->offset = file->currentOffset();
413         bitmasks[id] = sig;
414     } else if (file->currentOffset() < sig->offset) {
415         /* skip over the signature */
416         int num_flags = read_uint();
417         for (int i = 0; i < num_flags; ++i) {
418             skip_string(); /*name */
419             skip_uint(); /* value */
420         }
421     }
422
423     assert(sig);
424     return sig;
425 }
426
427
428 void Parser::parse_enter(Mode mode) {
429     unsigned thread_id;
430
431     if (version >= 4) {
432         thread_id = read_uint();
433     } else {
434         thread_id = 0;
435     }
436
437     FunctionSigFlags *sig = parse_function_sig();
438
439     Call *call = new Call(sig, sig->flags, thread_id);
440
441     call->no = next_call_no++;
442
443     if (parse_call_details(call, mode)) {
444         calls.push_back(call);
445     } else {
446         delete call;
447     }
448 }
449
450
451 Call *Parser::parse_leave(Mode mode) {
452     unsigned call_no = read_uint();
453     Call *call = NULL;
454     for (CallList::iterator it = calls.begin(); it != calls.end(); ++it) {
455         if ((*it)->no == call_no) {
456             call = *it;
457             calls.erase(it);
458             break;
459         }
460     }
461     if (!call) {
462         /* This might happen on random access, when an asynchronous call is stranded
463          * between two frames.  We won't return this call, but we still need to skip 
464          * over its data.
465          */
466         const FunctionSig sig = {0, NULL, 0, NULL};
467         call = new Call(&sig, 0, 0);
468         parse_call_details(call, SCAN);
469         delete call;
470         return NULL;
471     }
472
473     if (parse_call_details(call, mode)) {
474         return call;
475     } else {
476         delete call;
477         return NULL;
478     }
479 }
480
481
482 bool Parser::parse_call_details(Call *call, Mode mode) {
483     do {
484         int c = read_byte();
485         switch (c) {
486         case trace::CALL_END:
487 #if TRACE_VERBOSE
488             std::cerr << "\tCALL_END\n";
489 #endif
490             return true;
491         case trace::CALL_ARG:
492 #if TRACE_VERBOSE
493             std::cerr << "\tCALL_ARG\n";
494 #endif
495             parse_arg(call, mode);
496             break;
497         case trace::CALL_RET:
498 #if TRACE_VERBOSE
499             std::cerr << "\tCALL_RET\n";
500 #endif
501             call->ret = parse_value(mode);
502             break;
503         default:
504             std::cerr << "error: ("<<call->name()<< ") unknown call detail "
505                       << c << "\n";
506             exit(1);
507         case -1:
508             return false;
509         }
510     } while(true);
511 }
512
513
514 /**
515  * Make adjustments to this particular call flags.
516  *
517  * NOTE: This is called per-call so no string comparisons should be done here.
518  * All name comparisons should be done when the signature is parsed instead.
519  */
520 void Parser::adjust_call_flags(Call *call) {
521     // Mark glGetError() = GL_NO_ERROR as verbose
522     if (call->sig == glGetErrorSig &&
523         call->ret &&
524         call->ret->toSInt() == 0) {
525         call->flags |= CALL_FLAG_VERBOSE;
526     }
527 }
528
529 void Parser::parse_arg(Call *call, Mode mode) {
530     unsigned index = read_uint();
531     Value *value = parse_value(mode);
532     if (value) {
533         if (index >= call->args.size()) {
534             call->args.resize(index + 1);
535         }
536         call->args[index].value = value;
537     }
538 }
539
540
541 Value *Parser::parse_value(void) {
542     int c;
543     Value *value;
544     c = read_byte();
545     switch (c) {
546     case trace::TYPE_NULL:
547         value = new Null;
548         break;
549     case trace::TYPE_FALSE:
550         value = new Bool(false);
551         break;
552     case trace::TYPE_TRUE:
553         value = new Bool(true);
554         break;
555     case trace::TYPE_SINT:
556         value = parse_sint();
557         break;
558     case trace::TYPE_UINT:
559         value = parse_uint();
560         break;
561     case trace::TYPE_FLOAT:
562         value = parse_float();
563         break;
564     case trace::TYPE_DOUBLE:
565         value = parse_double();
566         break;
567     case trace::TYPE_STRING:
568         value = parse_string();
569         break;
570     case trace::TYPE_ENUM:
571         value = parse_enum();
572         break;
573     case trace::TYPE_BITMASK:
574         value = parse_bitmask();
575         break;
576     case trace::TYPE_ARRAY:
577         value = parse_array();
578         break;
579     case trace::TYPE_STRUCT:
580         value = parse_struct();
581         break;
582     case trace::TYPE_BLOB:
583         value = parse_blob();
584         break;
585     case trace::TYPE_OPAQUE:
586         value = parse_opaque();
587         break;
588     case trace::TYPE_REPR:
589         value = parse_repr();
590         break;
591     default:
592         std::cerr << "error: unknown type " << c << "\n";
593         exit(1);
594     case -1:
595         value = NULL;
596         break;
597     }
598 #if TRACE_VERBOSE
599     if (value) {
600         std::cerr << "\tVALUE ";
601         trace::dump(value, std::cerr);
602         std::cerr << "\n";
603     }
604 #endif
605     return value;
606 }
607
608
609 void Parser::scan_value(void) {
610     int c = read_byte();
611     switch (c) {
612     case trace::TYPE_NULL:
613     case trace::TYPE_FALSE:
614     case trace::TYPE_TRUE:
615         break;
616     case trace::TYPE_SINT:
617         scan_sint();
618         break;
619     case trace::TYPE_UINT:
620         scan_uint();
621         break;
622     case trace::TYPE_FLOAT:
623         scan_float();
624         break;
625     case trace::TYPE_DOUBLE:
626         scan_double();
627         break;
628     case trace::TYPE_STRING:
629         scan_string();
630         break;
631     case trace::TYPE_ENUM:
632         scan_enum();
633         break;
634     case trace::TYPE_BITMASK:
635         scan_bitmask();
636         break;
637     case trace::TYPE_ARRAY:
638         scan_array();
639         break;
640     case trace::TYPE_STRUCT:
641         scan_struct();
642         break;
643     case trace::TYPE_BLOB:
644         scan_blob();
645         break;
646     case trace::TYPE_OPAQUE:
647         scan_opaque();
648         break;
649     case trace::TYPE_REPR:
650         scan_repr();
651         break;
652     default:
653         std::cerr << "error: unknown type " << c << "\n";
654         exit(1);
655     case -1:
656         break;
657     }
658 }
659
660
661 Value *Parser::parse_sint() {
662     return new SInt(-(signed long long)read_uint());
663 }
664
665
666 void Parser::scan_sint() {
667     skip_uint();
668 }
669
670
671 Value *Parser::parse_uint() {
672     return new UInt(read_uint());
673 }
674
675
676 void Parser::scan_uint() {
677     skip_uint();
678 }
679
680
681 Value *Parser::parse_float() {
682     float value;
683     file->read(&value, sizeof value);
684     return new Float(value);
685 }
686
687
688 void Parser::scan_float() {
689     file->skip(sizeof(float));
690 }
691
692
693 Value *Parser::parse_double() {
694     double value;
695     file->read(&value, sizeof value);
696     return new Double(value);
697 }
698
699
700 void Parser::scan_double() {
701     file->skip(sizeof(double));
702 }
703
704
705 Value *Parser::parse_string() {
706     return new String(read_string());
707 }
708
709
710 void Parser::scan_string() {
711     skip_string();
712 }
713
714
715 Value *Parser::parse_enum() {
716     EnumSig *sig;
717     signed long long value;
718     if (version >= 3) {
719         sig = parse_enum_sig();
720         value = read_sint();
721     } else {
722         sig = parse_old_enum_sig();
723         assert(sig->num_values == 1);
724         value = sig->values->value;
725     }
726     return new Enum(sig, value);
727 }
728
729
730 void Parser::scan_enum() {
731     if (version >= 3) {
732         parse_enum_sig();
733         skip_sint();
734     } else {
735         parse_old_enum_sig();
736     }
737 }
738
739
740 Value *Parser::parse_bitmask() {
741     BitmaskSig *sig = parse_bitmask_sig();
742
743     unsigned long long value = read_uint();
744
745     return new Bitmask(sig, value);
746 }
747
748
749 void Parser::scan_bitmask() {
750     parse_bitmask_sig();
751     skip_uint(); /* value */
752 }
753
754
755 Value *Parser::parse_array(void) {
756     size_t len = read_uint();
757     Array *array = new Array(len);
758     for (size_t i = 0; i < len; ++i) {
759         array->values[i] = parse_value();
760     }
761     return array;
762 }
763
764
765 void Parser::scan_array(void) {
766     size_t len = read_uint();
767     for (size_t i = 0; i < len; ++i) {
768         scan_value();
769     }
770 }
771
772
773 Value *Parser::parse_blob(void) {
774     size_t size = read_uint();
775     Blob *blob = new Blob(size);
776     if (size) {
777         file->read(blob->buf, size);
778     }
779     return blob;
780 }
781
782
783 void Parser::scan_blob(void) {
784     size_t size = read_uint();
785     if (size) {
786         file->skip(size);
787     }
788 }
789
790
791 Value *Parser::parse_struct() {
792     StructSig *sig = parse_struct_sig();
793     Struct *value = new Struct(sig);
794
795     for (size_t i = 0; i < sig->num_members; ++i) {
796         value->members[i] = parse_value();
797     }
798
799     return value;
800 }
801
802
803 void Parser::scan_struct() {
804     StructSig *sig = parse_struct_sig();
805     for (size_t i = 0; i < sig->num_members; ++i) {
806         scan_value();
807     }
808 }
809
810
811 Value *Parser::parse_opaque() {
812     unsigned long long addr;
813     addr = read_uint();
814     return new Pointer(addr);
815 }
816
817
818 void Parser::scan_opaque() {
819     skip_uint();
820 }
821
822
823 Value *Parser::parse_repr() {
824     Value *humanValue = parse_value();
825     Value *machineValue = parse_value();
826     return new Repr(humanValue, machineValue);
827 }
828
829
830 void Parser::scan_repr() {
831     scan_value();
832     scan_value();
833 }
834
835
836 const char * Parser::read_string(void) {
837     size_t len = read_uint();
838     char * value = new char[len + 1];
839     if (len) {
840         file->read(value, len);
841     }
842     value[len] = 0;
843 #if TRACE_VERBOSE
844     std::cerr << "\tSTRING \"" << value << "\"\n";
845 #endif
846     return value;
847 }
848
849
850 void Parser::skip_string(void) {
851     size_t len = read_uint();
852     file->skip(len);
853 }
854
855
856 /*
857  * For the time being, a signed int is encoded as any other value, but we here parse
858  * it without the extra baggage of the Value class.
859  */
860 signed long long
861 Parser::read_sint(void) {
862     int c;
863     c = read_byte();
864     switch (c) {
865     case trace::TYPE_SINT:
866         return -(signed long long)read_uint();
867     case trace::TYPE_UINT:
868         return read_uint();
869     default:
870         std::cerr << "error: unexpected type " << c << "\n";
871         exit(1);
872     case -1:
873         return 0;
874     }
875 }
876
877 void
878 Parser::skip_sint(void) {
879     skip_byte();
880     skip_uint();
881 }
882
883 unsigned long long Parser::read_uint(void) {
884     unsigned long long value = 0;
885     int c;
886     unsigned shift = 0;
887     do {
888         c = file->getc();
889         if (c == -1) {
890             break;
891         }
892         value |= (unsigned long long)(c & 0x7f) << shift;
893         shift += 7;
894     } while(c & 0x80);
895 #if TRACE_VERBOSE
896     std::cerr << "\tUINT " << value << "\n";
897 #endif
898     return value;
899 }
900
901
902 void Parser::skip_uint(void) {
903     int c;
904     do {
905         c = file->getc();
906         if (c == -1) {
907             break;
908         }
909     } while(c & 0x80);
910 }
911
912
913 inline int Parser::read_byte(void) {
914     int c = file->getc();
915 #if TRACE_VERBOSE
916     if (c < 0)
917         std::cerr << "\tEOF" << "\n";
918     else
919         std::cerr << "\tBYTE 0x" << std::hex << c << std::dec << "\n";
920 #endif
921     return c;
922 }
923
924
925 inline void Parser::skip_byte(void) {
926     file->skip(1);
927 }
928
929
930 } /* namespace trace */