]> git.cworth.org Git - vogl/blob - src/voglsyms/voglsyms.cpp
Initial vogl checkin
[vogl] / src / voglsyms / voglsyms.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 #include "vogl_trace_file_reader.h"
27
28 #include "vogl_colorized_console.h"
29 #include "vogl_command_line_params.h"
30 #include "vogl_unique_ptr.h"
31
32 #include "btrace.h"
33
34 //$ TODO: Need to run voglsyms32 to resolve 32-bit symbols and voglsyms64 for 64-bit symbols.
35 //        This is going to be a decent bit of work modifying elf.c in libbacktrace though...
36
37 //$ TODO: Need to update the tracefile with the resolved symbols.
38
39 //----------------------------------------------------------------------------------------------------------------------
40 // globals
41 //----------------------------------------------------------------------------------------------------------------------
42 static cfile_stream *g_vogl_pLog_stream;
43
44 struct addr_data_t
45 {
46     uint32 index;
47     uint32 count;
48     vogl::vector<uintptr_t> addrs;
49 };
50
51 //----------------------------------------------------------------------------------------------------------------------
52 // command line params
53 //----------------------------------------------------------------------------------------------------------------------
54 static command_line_param_desc g_command_line_param_descs[] =
55     {
56       { "resolve_symbols", 0, false, "Resolve symbols and write backtrace_map_syms.json in trace file" },
57       { "logfile", 1, false, "Create logfile" },
58       { "logfile_append", 1, false, "Append output to logfile" },
59       { "help", 0, false, "Display this help" },
60       { "?", 0, false, "Display this help" },
61       { "pause", 0, false, "Wait for a key at startup (so a debugger can be attached)" },
62       { "verbose", 0, false, "Verbose debug output" },
63       { "quiet", 0, false, "Disable all console output" },
64     };
65
66 //----------------------------------------------------------------------------------------------------------------------
67 // init_logfile
68 //----------------------------------------------------------------------------------------------------------------------
69 static bool init_logfile()
70 {
71     VOGL_FUNC_TRACER
72
73     dynamic_string log_file(g_command_line_params.get_value_as_string_or_empty("logfile"));
74     dynamic_string log_file_append(g_command_line_params.get_value_as_string_or_empty("logfile_append"));
75     if (log_file.is_empty() && log_file_append.is_empty())
76         return true;
77
78     dynamic_string filename(log_file_append.is_empty() ? log_file : log_file_append);
79
80     // This purposely leaks, don't care
81     g_vogl_pLog_stream = vogl_new(cfile_stream);
82
83     if (!g_vogl_pLog_stream->open(filename.get_ptr(), cDataStreamWritable, !log_file_append.is_empty()))
84     {
85         vogl_error_printf("%s: Failed opening log file \"%s\"\n", VOGL_FUNCTION_NAME, filename.get_ptr());
86         return false;
87     }
88     else
89     {
90         vogl_message_printf("Opened log file \"%s\"\n", filename.get_ptr());
91         console::set_log_stream(g_vogl_pLog_stream);
92     }
93
94     return true;
95 }
96
97 //----------------------------------------------------------------------------------------------------------------------
98 // tool_print_title
99 //----------------------------------------------------------------------------------------------------------------------
100 static void tool_print_title()
101 {
102     VOGL_FUNC_TRACER
103
104     vogl_printf("voglsyms ");
105     if (sizeof(void *) > 4)
106         vogl_printf("64-bit ");
107     else
108         vogl_printf("32-bit ");
109 #ifdef VOGL_BUILD_DEBUG
110     vogl_printf("Debug ");
111 #else
112     vogl_printf("Release ");
113 #endif
114     vogl_printf("Built %s %s\n", __DATE__, __TIME__);
115 }
116
117 //----------------------------------------------------------------------------------------------------------------------
118 // tool_print_help
119 //----------------------------------------------------------------------------------------------------------------------
120 static void tool_print_help()
121 {
122     VOGL_FUNC_TRACER
123
124     vogl_printf("Usage: voglsyms [ -option ... ] input_file optional_output_file [ -option ... ]\n");
125     vogl_printf("Command line options may begin with single minus \"-\" or double minus \"--\"\n");
126
127     vogl_printf("\nCommand line options:\n");
128
129     dump_command_line_info(VOGL_ARRAY_SIZE(g_command_line_param_descs), g_command_line_param_descs, "--");
130 }
131
132 //----------------------------------------------------------------------------------------------------------------------
133 // init_command_line_params
134 //----------------------------------------------------------------------------------------------------------------------
135 static bool init_command_line_params(int argc, char *argv[])
136 {
137     VOGL_FUNC_TRACER
138
139     command_line_params::parse_config parse_cfg;
140     parse_cfg.m_single_minus_params = true;
141     parse_cfg.m_double_minus_params = true;
142
143     if (!g_command_line_params.parse(get_command_line_params(argc, argv),
144                                      VOGL_ARRAY_SIZE(g_command_line_param_descs),
145                                      g_command_line_param_descs, parse_cfg))
146     {
147         vogl_error_printf("%s: Failed parsing command line parameters!\n", VOGL_FUNCTION_NAME);
148         return false;
149     }
150
151     if (!init_logfile())
152         return false;
153
154     if (g_command_line_params.get_value_as_bool("help") || g_command_line_params.get_value_as_bool("?"))
155     {
156         tool_print_help();
157         return false;
158     }
159
160     return true;
161 }
162
163 //----------------------------------------------------------------------------------------------------------------------
164 // voglsyms_init
165 //----------------------------------------------------------------------------------------------------------------------
166 static bool voglsyms_init(int argc, char *argv[])
167 {
168     VOGL_FUNC_TRACER
169
170     g_thread_safe_random.seed_from_urandom();
171
172     console::disable_prefixes();
173
174     colorized_console::init();
175     colorized_console::set_exception_callback();
176     //console::set_tool_prefix("(voglsyms) ");
177
178     tool_print_title();
179
180     if (!init_command_line_params(argc, argv))
181         return false;
182
183     if (g_command_line_params.get_value_as_bool("quiet"))
184         console::disable_output();
185
186     return true;
187 }
188
189 //----------------------------------------------------------------------------------------------------------------------
190 // voglsyms_deinit
191 //----------------------------------------------------------------------------------------------------------------------
192 static void voglsyms_deinit()
193 {
194     VOGL_FUNC_TRACER
195
196     colorized_console::deinit();
197 }
198
199 //----------------------------------------------------------------------------------------------------------------------
200 // dump_compiler_info
201 //----------------------------------------------------------------------------------------------------------------------
202 static bool dump_compiler_info(vogl_trace_file_reader *pTrace_reader, dynamic_string &tracefile_arch)
203 {
204     if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_COMPILER_INFO_FILENAME))
205     {
206         vogl_header1_printf("%s\n", std::string(78, '*').c_str());
207         vogl_header1_printf("%s\n", VOGL_TRACE_ARCHIVE_COMPILER_INFO_FILENAME);
208         vogl_header1_printf("%s\n", std::string(78, '*').c_str());
209
210         uint8_vec machine_info_data;
211         if (pTrace_reader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_COMPILER_INFO_FILENAME, machine_info_data))
212         {
213             json_document doc;
214             json_node *pRoot = doc.get_root();
215
216             if (doc.deserialize((const char *)machine_info_data.get_ptr(), machine_info_data.size()) && pRoot->size())
217             {
218                 for (uint i = 0; i < pRoot->size(); i++)
219                 {
220                     const dynamic_string &sectionKeyStr = pRoot->get_key(i);
221
222                     vogl_message_printf("%s\n", sectionKeyStr.c_str());
223                     const vogl::json_value &value = pRoot->get_value(i);
224                     const json_node *pNode = value.get_node_ptr();
225
226                     // glinfo, uname, etc.
227                     for (uint i = 0; i < pNode->size(); i++)
228                     {
229                         dynamic_string key = pNode->get_key(i);
230                         const vogl::json_value &val = pNode->get_value(i);
231                         dynamic_string str = val.as_string();
232
233                         vogl_printf("  %s: %s\n", key.c_str(), str.c_str());
234
235                         if (!key.compare("arch"))
236                             tracefile_arch = str;
237                     }
238                 }
239
240                 vogl_printf("\n");
241                 return true;
242             }
243         }
244     }
245
246     return false;
247 }
248
249 //----------------------------------------------------------------------------------------------------------------------
250 // dump_machine_info
251 //----------------------------------------------------------------------------------------------------------------------
252 static bool dump_machine_info(vogl_trace_file_reader *pTrace_reader, vector<btrace_module_info> &module_infos)
253 {
254     if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_MACHINE_INFO_FILENAME))
255     {
256         vogl_header1_printf("%s\n", std::string(78, '*').c_str());
257         vogl_header1_printf("%s\n", VOGL_TRACE_ARCHIVE_MACHINE_INFO_FILENAME);
258         vogl_header1_printf("%s\n", std::string(78, '*').c_str());
259
260         uint8_vec machine_info_data;
261         if (pTrace_reader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_MACHINE_INFO_FILENAME, machine_info_data))
262         {
263             json_document doc;
264             json_node *pRoot = doc.get_root();
265
266             if (doc.deserialize((const char *)machine_info_data.get_ptr(), machine_info_data.size()) && pRoot->size())
267             {
268                 for (uint i = 0; i < pRoot->size(); i++)
269                 {
270                     const dynamic_string &sectionKeyStr = pRoot->get_key(i);
271
272                     const vogl::json_value &value = pRoot->get_value(i);
273                     const json_node *pNode = value.get_node_ptr();
274
275                     bool is_module_list = (sectionKeyStr == "module_list") && value.is_node();
276
277                     if (!is_module_list)
278                     {
279                         vogl_message_printf("%s\n", sectionKeyStr.c_str());
280                     }
281
282                     if (is_module_list)
283                     {
284                         for (uint i = 0; i < pNode->size(); i++)
285                         {
286                             dynamic_string key = pNode->get_key(i);
287                             const vogl::json_value &val = pNode->get_value(i);
288
289                             // key is filename, addr_base, addr_size, uuid, is_exe
290                             if (val.is_array())
291                             {
292                                 uint index = 0;
293                                 const json_node *pNode2 = val.get_node_ptr();
294                                 uint size = pNode2->size();
295
296                                 btrace_module_info module_info;
297
298                                 memset(&module_info, 0, sizeof(module_info));
299
300                                 module_info.filename = vogl::vogl_strdup(key.c_str());
301                                 module_info.base_address = (index < size) ? (uintptr_t)pNode2->get_value(index++).as_uint64() : 0;
302                                 module_info.address_size = (index < size) ? pNode2->get_value(index++).as_uint32() : 0;
303                                 const char *uuid_str = (index < size) ? pNode2->get_value(index++).as_string_ptr() : "--";
304                                 module_info.is_exe = (index < size) ? pNode2->get_value(index++).as_string() == "(exe)" : false;
305
306                                 module_info.uuid_len = btrace_uuid_str_to_uuid(module_info.uuid, uuid_str);
307                                 module_infos.push_back(module_info);
308                             }
309                         }
310                     }
311                     else if (value.is_array())
312                     {
313                         // environ_list, cmdline, etc.
314                         for (uint element = 0; element < pNode->size(); element++)
315                         {
316                             dynamic_string str = pNode->get_value(element).as_string();
317                             vogl_printf("  %s\n", str.c_str());
318                         }
319
320                         vogl_printf("\n");
321                     }
322                     else if (value.is_node())
323                     {
324                         // glinfo, uname, etc.
325                         for (uint i = 0; i < pNode->size(); i++)
326                         {
327                             dynamic_string key = pNode->get_key(i);
328                             const vogl::json_value &val = pNode->get_value(i);
329
330                             dynamic_string str = val.as_string();
331
332                             if (val.is_array())
333                             {
334                                 const json_node *pNode2 = val.get_node_ptr();
335
336                                 vogl_printf("%s\n", key.c_str());
337                                 for (uint element = 0; element < pNode2->size(); element++)
338                                 {
339                                     dynamic_string str = pNode2->get_value(element).as_string();
340                                     vogl_printf("  %s\n", str.c_str());
341                                 }
342                             }
343                             else
344                             {
345                                 vogl_printf("  %s: %s\n", key.c_str(), str.c_str());
346                             }
347                         }
348                         vogl_printf("\n");
349                     }
350                     else
351                     {
352                         dynamic_string str = value.as_string();
353                         vogl_printf("%s\n", str.c_str());
354                     }
355                 }
356             }
357
358             return true;
359         }
360     }
361
362     return false;
363 }
364
365 //----------------------------------------------------------------------------------------------------------------------
366 // get_backtrace_map_addrs
367 //----------------------------------------------------------------------------------------------------------------------
368 static bool get_backtrace_map_addrs(vogl_trace_file_reader *pTrace_reader, vector<addr_data_t> &addr_data_arr)
369 {
370     if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_ADDRS_FILENAME))
371     {
372         uint8_vec backtrace_data_addrs;
373
374         if (pTrace_reader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_ADDRS_FILENAME, backtrace_data_addrs))
375         {
376             json_document doc;
377             json_node *pRoot = doc.get_root();
378
379             if (doc.deserialize((const char *)backtrace_data_addrs.get_ptr(), backtrace_data_addrs.size()) && pRoot->size())
380             {
381                 for (uint i = 0; i < pRoot->size(); i++)
382                 {
383                     json_node *pChild = pRoot->get_child(i);
384                     json_node *pAddrs = pChild->find_child_array("addrs");
385
386                     addr_data_t addr_data;
387                     addr_data.index = 0;
388                     addr_data.count = 0;
389                     pChild->get_value_as_uint32("index", addr_data.index);
390                     pChild->get_value_as_uint32("count", addr_data.count);
391
392                     for (uint i = 0; i < pAddrs->size(); i++)
393                     {
394                         uintptr_t addr = (uintptr_t)pAddrs->get_value(i).as_uint64();
395                         addr_data.addrs.push_back(addr);
396                     }
397
398                     addr_data_arr.push_back(addr_data);
399                 }
400
401                 return true;
402             }
403         }
404     }
405
406     return false;
407 }
408
409 //----------------------------------------------------------------------------------------------------------------------
410 // dump_backtrace_map_syms
411 //----------------------------------------------------------------------------------------------------------------------
412 static bool dump_backtrace_map_syms(vogl_trace_file_reader *pTrace_reader)
413 {
414     if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_SYMS_FILENAME))
415     {
416         uint8_vec backtrace_data;
417
418         if (pTrace_reader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_SYMS_FILENAME, backtrace_data))
419         {
420             json_document doc;
421             json_node *pRoot = doc.get_root();
422
423             if (doc.deserialize((const char *)backtrace_data.get_ptr(), backtrace_data.size()) && pRoot->size())
424             {
425                 vogl_header1_printf("%s\n", std::string(78, '*').c_str());
426                 vogl_header1_printf("%s\n", VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_SYMS_FILENAME);
427                 vogl_header1_printf("%s\n", std::string(78, '*').c_str());
428
429                 doc.print(true, 0, 0);
430                 return true;
431             }
432         }
433     }
434
435     return false;
436 }
437
438 //----------------------------------------------------------------------------------------------------------------------
439 // voglsym_main_loop
440 //----------------------------------------------------------------------------------------------------------------------
441 static bool
442 voglsym_main_loop(char *argv[])
443 {
444     VOGL_FUNC_TRACER
445
446     dynamic_string tracefile_arch;
447     dynamic_string actual_trace_filename;
448     vector<addr_data_t> addr_data_arr;
449     vector<btrace_module_info> module_infos;
450
451     dynamic_string trace_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
452     if (trace_filename.is_empty())
453     {
454         vogl_error_printf("%s: No trace file specified!\n", VOGL_FUNCTION_NAME);
455         return false;
456     }
457
458     vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(
459         trace_filename,
460         actual_trace_filename,
461         g_command_line_params.get_value_as_string_or_empty("loose_file_path").get_ptr()));
462     if (!pTrace_reader.get())
463     {
464         vogl_error_printf("%s: File not found, or unable to determine file type of trace file \"%s\"\n", VOGL_FUNCTION_NAME, trace_filename.get_ptr());
465         return false;
466     }
467
468     bool resolve_symbols = g_command_line_params.get_value_as_bool("resolve_symbols");
469
470     if (resolve_symbols)
471         vogl_printf("Resolving symbols in trace file %s\n\n", actual_trace_filename.get_ptr());
472     else
473         vogl_printf("Reading trace file %s\n\n", actual_trace_filename.get_ptr());
474
475     // compiler_info.json
476     dump_compiler_info(pTrace_reader.get(), tracefile_arch);
477
478     if (resolve_symbols)
479     {
480         if (tracefile_arch.size())
481         {
482             bool is_64bit = (sizeof(void *) == 8);
483             bool trace_file_arch_is_64bits = !tracefile_arch.compare("64bit");
484
485             if (trace_file_arch_is_64bits != is_64bit)
486             {
487                 const char *arch_str = is_64bit ? "64-bit" : "32-bit";
488                 vogl_error_printf("ERROR: %s is %s, tracefile is %s.\n", argv[0], arch_str, tracefile_arch.c_str());
489                 vogl_error_printf("ERROR: Same architecture required to resolve symbols.\n");
490                 return -1;
491             }
492         }
493
494         if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_SYMS_FILENAME))
495         {
496             vogl_error_printf("ERROR: Symbols have already resolved for this tracefile.\n");
497             return -1;
498         }
499     }
500
501     // machine_info.json
502     dump_machine_info(pTrace_reader.get(), module_infos);
503
504     // backtrace_map_addrs.json
505     get_backtrace_map_addrs(pTrace_reader.get(), addr_data_arr);
506
507     // backtrace_map_syms.json
508     dump_backtrace_map_syms(pTrace_reader.get());
509
510     // Spew our module information
511     if (module_infos.size())
512     {
513         vogl_header1_printf("%s\n", std::string(78, '*').c_str());
514         vogl_header1_printf("%s\n", "Modules");
515         vogl_header1_printf("%s\n", std::string(78, '*').c_str());
516
517         for (uint i = 0; i < module_infos.size(); i++)
518         {
519             char uuid_str[41];
520             const btrace_module_info &module_info = module_infos[i];
521
522             btrace_uuid_to_str(uuid_str, module_info.uuid, module_info.uuid_len);
523
524             vogl_printf("0x%" PRIxPTR " (%u) %s %s %s",
525                        module_info.base_address, module_info.address_size, uuid_str,
526                        module_info.filename, module_info.is_exe ? "(exe)" : "");
527
528             if (resolve_symbols)
529             {
530                 const char *debug_filename = NULL;
531                 if (btrace_dlopen_add_module(module_info))
532                 {
533                     debug_filename = btrace_get_debug_filename(module_info.filename);
534                 }
535
536                 if (debug_filename)
537                 {
538                     vogl_printf(" [%s]", debug_filename);
539                 }
540             }
541
542             vogl_printf("\n");
543         }
544
545         vogl_printf("\n");
546     }
547
548     // Spew our backtrace addresses
549     if (addr_data_arr.size())
550     {
551         vogl_header1_printf("%s\n", std::string(78, '*').c_str());
552         vogl_header1_printf("%s\n", VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_ADDRS_FILENAME);
553         vogl_header1_printf("%s\n", std::string(78, '*').c_str());
554
555         vogl_printf("index (count): addr0, addr1, ...\n");
556         for (uint i = 0; i < addr_data_arr.size(); i++)
557         {
558             const addr_data_t &addr_data = addr_data_arr[i];
559
560             vogl_printf("0x%x (%u): ", addr_data.index, addr_data.count);
561             for (uint j = 0; j < addr_data.addrs.size(); j++)
562             {
563                 vogl_printf("%" PRIxPTR " ", addr_data.addrs[j]);
564             }
565             vogl_printf("\n");
566         }
567
568         vogl_printf("\n");
569     }
570
571     // Resolve symbols if we're supposed to.
572     //$ TODO: The resolve symbols should be added to the trace file?
573     if (resolve_symbols)
574     {
575         json_document doc;
576         json_node *pRoot = doc.get_root();
577
578         pRoot->init_array();
579
580         vogl_header1_printf("%s\n", std::string(78, '*').c_str());
581         vogl_header1_printf("%s\n", "Resolving symbols...");
582         vogl_header1_printf("%s\n", std::string(78, '*').c_str());
583
584         for (uint i = 0; i < addr_data_arr.size(); i++)
585         {
586             const addr_data_t &addr_data = addr_data_arr[i];
587             json_node &syms_arr = pRoot->add_array();
588
589             for (uint j = 0; j < addr_data.addrs.size(); j++)
590             {
591                 btrace_info trace_info;
592                 uintptr_t addr = addr_data.addrs[j];
593                 bool success = btrace_resolve_addr(&trace_info, addr,
594                                                    BTRACE_RESOLVE_ADDR_GET_FILENAME | BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC);
595
596                 dynamic_string sym;
597                 if (!success)
598                 {
599                     sym = "?";
600                 }
601                 else if (trace_info.function[0] && trace_info.filename[0])
602                 {
603                     // Got function and/or filename.
604                     sym.format("%s (%s+0x%" PRIx64 ") at %s:%i",
605                                trace_info.function,
606                                trace_info.module[0] ? trace_info.module : "?",
607                                cast_val_to_uint64(trace_info.offset),
608                                trace_info.filename,
609                                trace_info.linenumber);
610                 }
611                 else if (trace_info.function[0])
612                 {
613                     // Got function, no filename.
614                     sym.format("%s (%s+0x%" PRIx64 ")",
615                                trace_info.function,
616                                trace_info.module[0] ? trace_info.module : "?",
617                                cast_val_to_uint64(trace_info.offset));
618                 }
619                 else
620                 {
621                     // Only got modulename (no debugging information found).
622                     sym.format("(%s+0x%" PRIx64 ")",
623                                trace_info.module[0] ? trace_info.module : "?",
624                                cast_val_to_uint64(trace_info.offset));
625                 }
626
627                 syms_arr.add_value(sym);
628             }
629         }
630
631         doc.print(true, 0, 0);
632     }
633
634     return true;
635 }
636
637 //----------------------------------------------------------------------------------------------------------------------
638 // main
639 //----------------------------------------------------------------------------------------------------------------------
640 int main(int argc, char *argv[])
641 {
642 #if VOGL_FUNCTION_TRACING
643     fflush(stdout);
644     fflush(stderr);
645     setvbuf(stdout, NULL, _IONBF, 0);
646     setvbuf(stderr, NULL, _IONBF, 0);
647 #endif
648
649     VOGL_FUNC_TRACER
650
651     if (!voglsyms_init(argc, argv))
652     {
653         voglsyms_deinit();
654         return EXIT_FAILURE;
655     }
656
657     if (g_command_line_params.get_count("") < 2)
658     {
659         vogl_error_printf("No trace file specified!\n");
660
661         tool_print_help();
662
663         voglsyms_deinit();
664         return EXIT_FAILURE;
665     }
666
667     if (g_command_line_params.get_value_as_bool("pause"))
668     {
669         vogl_message_printf("Press key to continue\n");
670         vogl_sleep(1000);
671         getchar();
672     }
673
674     bool success = voglsym_main_loop(argv);
675
676     voglsyms_deinit();
677
678     return success ? EXIT_SUCCESS : EXIT_FAILURE;
679 }