Showing the changes to src/exec/debugger.cpp (1 of 10 files). Show all
Unified · Split
| @@ -5,6 +5,7 @@ · 4 unmodified lines | |||
| 5 | // | 5 | // |
| 6 | 6 | ||
| 7 | #include <iostream> | 7 | #include <iostream> |
| 8 | #include <fstream> | ||
| 8 | #include <iomanip> | 9 | #include <iomanip> |
| 9 | #include <sstream> | 10 | #include <sstream> |
| 10 | #include <string> | 11 | #include <string> |
| @@ -117,16 +118,26 @@ namespace · 106 unmodified lines | |||
| 117 | dbg_cmd_exec_t exec; | 118 | dbg_cmd_exec_t exec; |
| 118 | }; | 119 | }; |
| 119 | 120 | ||
| 120 | std::string stack_to_string(const vm_registers_t &r) | 121 | std::string stack_to_string(const vm_registers_t &r, debugger &dbg) |
| 121 | { | 122 | { |
| 122 | auto msg = std::stringstream{}; | 123 | auto msg = std::stringstream{}; |
| 123 | walk_stack(r, [&msg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref) | 124 | walk_stack(r, [&msg, &dbg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref) |
| 124 | { | 125 | { |
| 125 | auto module_name = "<No Name>"; | 126 | auto resolved_name = dbg.resolve_function_source_line(module_ref.module, pc); |
| 126 | if (module_ref.module->module_name != nullptr) | 127 | if (!resolved_name.empty()) |
| 127 | module_name = module_ref.module->module_name->str(); | 128 | { |
| 129 | msg << resolved_name; | ||
| 130 | } | ||
| 131 | else | ||
| 132 | { | ||
| 133 | auto module_name = "<No Name>"; | ||
| 134 | if (module_ref.module->module_name != nullptr) | ||
| 135 | module_name = module_ref.module->module_name->str(); | ||
| 136 | |||
| 137 | msg << module_name << "@" << pc; | ||
| 138 | } | ||
| 128 | 139 | ||
| 129 | msg << module_name << "@" << pc << "\n"; | 140 | msg << "\n"; |
| 130 | return true; | 141 | return true; |
| 131 | }); | 142 | }); |
| 132 | 143 | ||
| @@ -215,7 +226,7 @@ namespace · 82 unmodified lines | |||
| 215 | ss << "channel\n" | 226 | ss << "channel\n" |
| 216 | << "\tbuffer size: " << c->get_buffer_size() << "\n"; | 227 | << "\tbuffer size: " << c->get_buffer_size() << "\n"; |
| 217 | 228 | ||
| 218 | return ss; | 229 | return ss; |
| 219 | } | 230 | } |
| 220 | 231 | ||
| 221 | std::ostream& operator<<(std::ostream &ss, const vm_alloc_t *alloc) | 232 | std::ostream& operator<<(std::ostream &ss, const vm_alloc_t *alloc) |
| @@ -330,7 +341,7 @@ namespace · 108 unmodified lines | |||
| 330 | auto module_instance = m.module.lock(); | 341 | auto module_instance = m.module.lock(); |
| 331 | if (module_instance != nullptr) | 342 | if (module_instance != nullptr) |
| 332 | { | 343 | { |
| 333 | msg << std::setw(3) << m.load_id << ": " | 344 | msg << std::setw(3) << m.vm_id << ": " |
| 334 | << std::setw(16) << module_instance->module_name->str() | 345 | << std::setw(16) << module_instance->module_name->str() |
| 335 | << std::setw(0) << " - " << m.origin->str() << "\n"; | 346 | << std::setw(0) << " - " << m.origin->str() << "\n"; |
| 336 | } | 347 | } |
| @@ -372,7 +383,7 @@ namespace · 35 unmodified lines | |||
| 372 | 383 | ||
| 373 | cxt.vm.enum_loaded_modules([module_id, &module](const loaded_vm_module_t &m) | 384 | cxt.vm.enum_loaded_modules([module_id, &module](const loaded_vm_module_t &m) |
| 374 | { | 385 | { |
| 375 | if (module_id != m.load_id) | 386 | if (module_id != m.vm_id) |
| 376 | return true; | 387 | return true; |
| 377 | 388 | ||
| 378 | // Found the matching ID so stop | 389 | // Found the matching ID so stop |
| @@ -500,7 +511,7 @@ namespace · 121 unmodified lines | |||
| 500 | for (auto t : thread_registers) | 511 | for (auto t : thread_registers) |
| 501 | { | 512 | { |
| 502 | msg << "Thread " << t.first << ":\n"; | 513 | msg << "Thread " << t.first << ":\n"; |
| 503 | msg << stack_to_string(*t.second); | 514 | msg << stack_to_string(*t.second, cxt.dbg); |
| 504 | msg << "\n"; | 515 | msg << "\n"; |
| 505 | } | 516 | } |
| 506 | 517 | ||
| @@ -856,6 +867,30 @@ debugger::debugger(const debugger_options opt) · 349 unmodified lines | |||
| 856 | , controller{ nullptr } | 867 | , controller{ nullptr } |
| 857 | { } | 868 | { } |
| 858 | 869 | ||
| 870 | std::string debugger::resolve_function_source_line(std::shared_ptr<const vm_module_t> m, vm_pc_t pc) | ||
| 871 | { | ||
| 872 | if (m == nullptr) | ||
| 873 | return{}; | ||
| 874 | |||
| 875 | auto iter = _vm_id_to_symbol_debug.find(m->vm_id); | ||
| 876 | if (iter == std::end(_vm_id_to_symbol_debug)) | ||
| 877 | return{}; | ||
| 878 | |||
| 879 | std::stringstream ss; | ||
| 880 | auto &d = *(iter->second); | ||
| 881 | |||
| 882 | // Position the debug symbol iterator | ||
| 883 | d.set_current_pc(pc); | ||
| 884 | |||
| 885 | ss << m->module_name->str() | ||
| 886 | << '!' | ||
| 887 | << d.current_function_name() | ||
| 888 | << ' ' | ||
| 889 | << d.current_source_location().begin_line; | ||
| 890 | |||
| 891 | return ss.str(); | ||
| 892 | } | ||
| 893 | |||
| 859 | void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id) | 894 | void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id) |
| 860 | { | 895 | { |
| 861 | _tool_id = tool_id; | 896 | _tool_id = tool_id; |
| @@ -909,6 +944,13 @@ void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id) · 47 unmodified lines | |||
| 909 | prompt(*this, *cxt.value1.registers); | 944 | prompt(*this, *cxt.value1.registers); |
| 910 | })); | 945 | })); |
| 911 | 946 | ||
| 947 | _event_cookies.emplace(controller->subscribe_event(vm_event_t::module_vm_load, [&](vm_event_t, vm_event_context_t &cxt) | ||
| 948 | { | ||
| 949 | auto loaded_mod = cxt.value1.loaded_mod; | ||
| 950 | assert(loaded_mod != nullptr); | ||
| 951 | load_symbols(*loaded_mod); | ||
| 952 | })); | ||
| 953 | |||
| 912 | _event_cookies.emplace(controller->subscribe_event(vm_event_t::module_thread_load, [&](vm_event_t, vm_event_context_t &cxt) | 954 | _event_cookies.emplace(controller->subscribe_event(vm_event_t::module_thread_load, [&](vm_event_t, vm_event_context_t &cxt) |
| 913 | { | 955 | { |
| 914 | auto m = (*cxt.value2.module); | 956 | auto m = (*cxt.value2.module); |
| @@ -960,4 +1002,73 @@ void debugger::on_unload() · 45 unmodified lines | |||
| 960 | controller->unsubscribe_event(ec); | 1002 | controller->unsubscribe_event(ec); |
| 961 | 1003 | ||
| 962 | controller = nullptr; | 1004 | controller = nullptr; |
| 1005 | } | ||
| 1006 | |||
| 1007 | void debugger::load_symbols(const disvm::loaded_vm_module_t &loaded_mod) | ||
| 1008 | { | ||
| 1009 | auto m = loaded_mod.module.lock(); | ||
| 1010 | assert(m != nullptr && "Loaded module event should have valid module reference"); | ||
| 1011 | |||
| 1012 | auto symbol_extension = ".sbl"; | ||
| 1013 | std::string symbol_path_maybe{ loaded_mod.origin->str() }; | ||
| 1014 | const auto ext_index = symbol_path_maybe.find_last_of('.'); | ||
| 1015 | if (ext_index == std::string::npos) | ||
| 1016 | { | ||
| 1017 | // Module doesn't have an extension so just append the symbol extension. | ||
| 1018 | symbol_path_maybe.append(symbol_extension); | ||
| 1019 | } | ||
| 1020 | else | ||
| 1021 | { | ||
| 1022 | // Replace the module extension with the symbol extension. | ||
| 1023 | symbol_path_maybe.replace(ext_index, std::string::npos, symbol_extension); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | std::ifstream sbl_file{ symbol_path_maybe, std::ifstream::in }; | ||
| 1027 | if (!sbl_file.is_open()) | ||
| 1028 | return; | ||
| 1029 | |||
| 1030 | std::unique_ptr<symbol::symbol_data_t> symbol_data; | ||
| 1031 | try | ||
| 1032 | { | ||
| 1033 | symbol_data = symbol::read(sbl_file); | ||
| 1034 | } | ||
| 1035 | catch (const std::runtime_error &e) | ||
| 1036 | { | ||
| 1037 | // File with assumed symbol name/extension is not the correct format | ||
| 1038 | std::stringstream msg; | ||
| 1039 | msg << "Assumed symbol file '" | ||
| 1040 | << symbol_path_maybe | ||
| 1041 | << "' has invalid symbol format. (" | ||
| 1042 | << e.what() | ||
| 1043 | << ")"; | ||
| 1044 | debug_print_error(msg.str()); | ||
| 1045 | return; | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | // Check if the symbol data matches the loaded module | ||
| 1049 | if (0 != symbol_data->get_module_name().compare(m->module_name->str()) | ||
| 1050 | || symbol_data->get_instruction_count() != m->code_section.size()) | ||
| 1051 | { | ||
| 1052 | // Symbol file does not match the loaded modules - stale file? | ||
| 1053 | std::stringstream msg; | ||
| 1054 | msg << "Symbol file '" | ||
| 1055 | << symbol_path_maybe | ||
| 1056 | << "' does not match loaded module '" | ||
| 1057 | << loaded_mod.origin->str() | ||
| 1058 | << "'."; | ||
| 1059 | debug_print_error(msg.str()); | ||
| 1060 | return; | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | const auto vm_id = loaded_mod.vm_id; | ||
| 1064 | assert(_vm_id_to_symbol_debug.find(vm_id) == std::end(_vm_id_to_symbol_debug) && "Module VM ID should be unique"); | ||
| 1065 | _vm_id_to_symbol_debug[vm_id] = symbol_data->get_debug(); | ||
| 1066 | |||
| 1067 | std::stringstream msg; | ||
| 1068 | msg << "Symbol file '" | ||
| 1069 | << symbol_path_maybe | ||
| 1070 | << "' loaded for '" | ||
| 1071 | << loaded_mod.origin->str() | ||
| 1072 | << "'."; | ||
| 1073 | debug_print_info(msg.str()); | ||
| 963 | } | 1074 | } |