1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 **************************************************************************/
26 #include "vogl_trace_file_reader.h"
28 #include "vogl_colorized_console.h"
29 #include "vogl_command_line_params.h"
30 #include "vogl_unique_ptr.h"
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...
37 //$ TODO: Need to update the tracefile with the resolved symbols.
39 //----------------------------------------------------------------------------------------------------------------------
41 //----------------------------------------------------------------------------------------------------------------------
42 static cfile_stream *g_vogl_pLog_stream;
48 vogl::vector<uintptr_t> addrs;
51 //----------------------------------------------------------------------------------------------------------------------
52 // command line params
53 //----------------------------------------------------------------------------------------------------------------------
54 static command_line_param_desc g_command_line_param_descs[] =
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" },
66 //----------------------------------------------------------------------------------------------------------------------
68 //----------------------------------------------------------------------------------------------------------------------
69 static bool init_logfile()
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())
78 dynamic_string filename(log_file_append.is_empty() ? log_file : log_file_append);
80 // This purposely leaks, don't care
81 g_vogl_pLog_stream = vogl_new(cfile_stream);
83 if (!g_vogl_pLog_stream->open(filename.get_ptr(), cDataStreamWritable, !log_file_append.is_empty()))
85 vogl_error_printf("%s: Failed opening log file \"%s\"\n", VOGL_FUNCTION_NAME, filename.get_ptr());
90 vogl_message_printf("Opened log file \"%s\"\n", filename.get_ptr());
91 console::set_log_stream(g_vogl_pLog_stream);
97 //----------------------------------------------------------------------------------------------------------------------
99 //----------------------------------------------------------------------------------------------------------------------
100 static void tool_print_title()
104 vogl_printf("voglsyms ");
105 if (sizeof(void *) > 4)
106 vogl_printf("64-bit ");
108 vogl_printf("32-bit ");
109 #ifdef VOGL_BUILD_DEBUG
110 vogl_printf("Debug ");
112 vogl_printf("Release ");
114 vogl_printf("Built %s %s\n", __DATE__, __TIME__);
117 //----------------------------------------------------------------------------------------------------------------------
119 //----------------------------------------------------------------------------------------------------------------------
120 static void tool_print_help()
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");
127 vogl_printf("\nCommand line options:\n");
129 dump_command_line_info(VOGL_ARRAY_SIZE(g_command_line_param_descs), g_command_line_param_descs, "--");
132 //----------------------------------------------------------------------------------------------------------------------
133 // init_command_line_params
134 //----------------------------------------------------------------------------------------------------------------------
135 static bool init_command_line_params(int argc, char *argv[])
139 command_line_params::parse_config parse_cfg;
140 parse_cfg.m_single_minus_params = true;
141 parse_cfg.m_double_minus_params = true;
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))
147 vogl_error_printf("%s: Failed parsing command line parameters!\n", VOGL_FUNCTION_NAME);
154 if (g_command_line_params.get_value_as_bool("help") || g_command_line_params.get_value_as_bool("?"))
163 //----------------------------------------------------------------------------------------------------------------------
165 //----------------------------------------------------------------------------------------------------------------------
166 static bool voglsyms_init(int argc, char *argv[])
170 g_thread_safe_random.seed_from_urandom();
172 console::disable_prefixes();
174 colorized_console::init();
175 colorized_console::set_exception_callback();
176 //console::set_tool_prefix("(voglsyms) ");
180 if (!init_command_line_params(argc, argv))
183 if (g_command_line_params.get_value_as_bool("quiet"))
184 console::disable_output();
189 //----------------------------------------------------------------------------------------------------------------------
191 //----------------------------------------------------------------------------------------------------------------------
192 static void voglsyms_deinit()
196 colorized_console::deinit();
199 //----------------------------------------------------------------------------------------------------------------------
200 // dump_compiler_info
201 //----------------------------------------------------------------------------------------------------------------------
202 static bool dump_compiler_info(vogl_trace_file_reader *pTrace_reader, dynamic_string &tracefile_arch)
204 if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_COMPILER_INFO_FILENAME))
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());
210 uint8_vec machine_info_data;
211 if (pTrace_reader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_COMPILER_INFO_FILENAME, machine_info_data))
214 json_node *pRoot = doc.get_root();
216 if (doc.deserialize((const char *)machine_info_data.get_ptr(), machine_info_data.size()) && pRoot->size())
218 for (uint i = 0; i < pRoot->size(); i++)
220 const dynamic_string §ionKeyStr = pRoot->get_key(i);
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();
226 // glinfo, uname, etc.
227 for (uint i = 0; i < pNode->size(); i++)
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();
233 vogl_printf(" %s: %s\n", key.c_str(), str.c_str());
235 if (!key.compare("arch"))
236 tracefile_arch = str;
249 //----------------------------------------------------------------------------------------------------------------------
251 //----------------------------------------------------------------------------------------------------------------------
252 static bool dump_machine_info(vogl_trace_file_reader *pTrace_reader, vector<btrace_module_info> &module_infos)
254 if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_MACHINE_INFO_FILENAME))
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());
260 uint8_vec machine_info_data;
261 if (pTrace_reader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_MACHINE_INFO_FILENAME, machine_info_data))
264 json_node *pRoot = doc.get_root();
266 if (doc.deserialize((const char *)machine_info_data.get_ptr(), machine_info_data.size()) && pRoot->size())
268 for (uint i = 0; i < pRoot->size(); i++)
270 const dynamic_string §ionKeyStr = pRoot->get_key(i);
272 const vogl::json_value &value = pRoot->get_value(i);
273 const json_node *pNode = value.get_node_ptr();
275 bool is_module_list = (sectionKeyStr == "module_list") && value.is_node();
279 vogl_message_printf("%s\n", sectionKeyStr.c_str());
284 for (uint i = 0; i < pNode->size(); i++)
286 dynamic_string key = pNode->get_key(i);
287 const vogl::json_value &val = pNode->get_value(i);
289 // key is filename, addr_base, addr_size, uuid, is_exe
293 const json_node *pNode2 = val.get_node_ptr();
294 uint size = pNode2->size();
296 btrace_module_info module_info;
298 memset(&module_info, 0, sizeof(module_info));
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;
306 module_info.uuid_len = btrace_uuid_str_to_uuid(module_info.uuid, uuid_str);
307 module_infos.push_back(module_info);
311 else if (value.is_array())
313 // environ_list, cmdline, etc.
314 for (uint element = 0; element < pNode->size(); element++)
316 dynamic_string str = pNode->get_value(element).as_string();
317 vogl_printf(" %s\n", str.c_str());
322 else if (value.is_node())
324 // glinfo, uname, etc.
325 for (uint i = 0; i < pNode->size(); i++)
327 dynamic_string key = pNode->get_key(i);
328 const vogl::json_value &val = pNode->get_value(i);
330 dynamic_string str = val.as_string();
334 const json_node *pNode2 = val.get_node_ptr();
336 vogl_printf("%s\n", key.c_str());
337 for (uint element = 0; element < pNode2->size(); element++)
339 dynamic_string str = pNode2->get_value(element).as_string();
340 vogl_printf(" %s\n", str.c_str());
345 vogl_printf(" %s: %s\n", key.c_str(), str.c_str());
352 dynamic_string str = value.as_string();
353 vogl_printf("%s\n", str.c_str());
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)
370 if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_ADDRS_FILENAME))
372 uint8_vec backtrace_data_addrs;
374 if (pTrace_reader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_ADDRS_FILENAME, backtrace_data_addrs))
377 json_node *pRoot = doc.get_root();
379 if (doc.deserialize((const char *)backtrace_data_addrs.get_ptr(), backtrace_data_addrs.size()) && pRoot->size())
381 for (uint i = 0; i < pRoot->size(); i++)
383 json_node *pChild = pRoot->get_child(i);
384 json_node *pAddrs = pChild->find_child_array("addrs");
386 addr_data_t addr_data;
389 pChild->get_value_as_uint32("index", addr_data.index);
390 pChild->get_value_as_uint32("count", addr_data.count);
392 for (uint i = 0; i < pAddrs->size(); i++)
394 uintptr_t addr = (uintptr_t)pAddrs->get_value(i).as_uint64();
395 addr_data.addrs.push_back(addr);
398 addr_data_arr.push_back(addr_data);
409 //----------------------------------------------------------------------------------------------------------------------
410 // dump_backtrace_map_syms
411 //----------------------------------------------------------------------------------------------------------------------
412 static bool dump_backtrace_map_syms(vogl_trace_file_reader *pTrace_reader)
414 if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_SYMS_FILENAME))
416 uint8_vec backtrace_data;
418 if (pTrace_reader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_SYMS_FILENAME, backtrace_data))
421 json_node *pRoot = doc.get_root();
423 if (doc.deserialize((const char *)backtrace_data.get_ptr(), backtrace_data.size()) && pRoot->size())
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());
429 doc.print(true, 0, 0);
438 //----------------------------------------------------------------------------------------------------------------------
440 //----------------------------------------------------------------------------------------------------------------------
442 voglsym_main_loop(char *argv[])
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;
451 dynamic_string trace_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
452 if (trace_filename.is_empty())
454 vogl_error_printf("%s: No trace file specified!\n", VOGL_FUNCTION_NAME);
458 vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(
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())
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());
468 bool resolve_symbols = g_command_line_params.get_value_as_bool("resolve_symbols");
471 vogl_printf("Resolving symbols in trace file %s\n\n", actual_trace_filename.get_ptr());
473 vogl_printf("Reading trace file %s\n\n", actual_trace_filename.get_ptr());
475 // compiler_info.json
476 dump_compiler_info(pTrace_reader.get(), tracefile_arch);
480 if (tracefile_arch.size())
482 bool is_64bit = (sizeof(void *) == 8);
483 bool trace_file_arch_is_64bits = !tracefile_arch.compare("64bit");
485 if (trace_file_arch_is_64bits != is_64bit)
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");
494 if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_SYMS_FILENAME))
496 vogl_error_printf("ERROR: Symbols have already resolved for this tracefile.\n");
502 dump_machine_info(pTrace_reader.get(), module_infos);
504 // backtrace_map_addrs.json
505 get_backtrace_map_addrs(pTrace_reader.get(), addr_data_arr);
507 // backtrace_map_syms.json
508 dump_backtrace_map_syms(pTrace_reader.get());
510 // Spew our module information
511 if (module_infos.size())
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());
517 for (uint i = 0; i < module_infos.size(); i++)
520 const btrace_module_info &module_info = module_infos[i];
522 btrace_uuid_to_str(uuid_str, module_info.uuid, module_info.uuid_len);
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)" : "");
530 const char *debug_filename = NULL;
531 if (btrace_dlopen_add_module(module_info))
533 debug_filename = btrace_get_debug_filename(module_info.filename);
538 vogl_printf(" [%s]", debug_filename);
548 // Spew our backtrace addresses
549 if (addr_data_arr.size())
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());
555 vogl_printf("index (count): addr0, addr1, ...\n");
556 for (uint i = 0; i < addr_data_arr.size(); i++)
558 const addr_data_t &addr_data = addr_data_arr[i];
560 vogl_printf("0x%x (%u): ", addr_data.index, addr_data.count);
561 for (uint j = 0; j < addr_data.addrs.size(); j++)
563 vogl_printf("%" PRIxPTR " ", addr_data.addrs[j]);
571 // Resolve symbols if we're supposed to.
572 //$ TODO: The resolve symbols should be added to the trace file?
576 json_node *pRoot = doc.get_root();
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());
584 for (uint i = 0; i < addr_data_arr.size(); i++)
586 const addr_data_t &addr_data = addr_data_arr[i];
587 json_node &syms_arr = pRoot->add_array();
589 for (uint j = 0; j < addr_data.addrs.size(); j++)
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);
601 else if (trace_info.function[0] && trace_info.filename[0])
603 // Got function and/or filename.
604 sym.format("%s (%s+0x%" PRIx64 ") at %s:%i",
606 trace_info.module[0] ? trace_info.module : "?",
607 cast_val_to_uint64(trace_info.offset),
609 trace_info.linenumber);
611 else if (trace_info.function[0])
613 // Got function, no filename.
614 sym.format("%s (%s+0x%" PRIx64 ")",
616 trace_info.module[0] ? trace_info.module : "?",
617 cast_val_to_uint64(trace_info.offset));
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));
627 syms_arr.add_value(sym);
631 doc.print(true, 0, 0);
637 //----------------------------------------------------------------------------------------------------------------------
639 //----------------------------------------------------------------------------------------------------------------------
640 int main(int argc, char *argv[])
642 #if VOGL_FUNCTION_TRACING
645 setvbuf(stdout, NULL, _IONBF, 0);
646 setvbuf(stderr, NULL, _IONBF, 0);
651 if (!voglsyms_init(argc, argv))
657 if (g_command_line_params.get_count("") < 2)
659 vogl_error_printf("No trace file specified!\n");
667 if (g_command_line_params.get_value_as_bool("pause"))
669 vogl_message_printf("Press key to continue\n");
674 bool success = voglsym_main_loop(argv);
678 return success ? EXIT_SUCCESS : EXIT_FAILURE;