History

Added vm_id to vm_module_t that is used by the VM to uniquely identify a module Updated tool dispatcher to pass on the loaded_vm_module_t type as the module_vm_load event argument Debugger now uses symbol data to resolve stack IPs

arr committed 9 years ago

Commit 72f24ec1 parent a3c184b10 files · +160 −35Browse files

Unified · Split

10 files
  1. src/asm/sbl_reader.cpp +6 −7
  2. src/exec/debugger.cpp +121 −10
  3. src/exec/exec.h +9 −3
  4. src/include/disvm.h +2 −2
  5. src/include/runtime.h +6 −0
  6. src/include/vm_asm.h +3 −0
  7. src/include/vm_tools.h +2 −2
  8. src/vm/tool_dispatch.cpp +2 −5
  9. src/vm/tool_dispatch.h +2 −2
  10. src/vm/vm.cpp +7 −4
src/asm/sbl_reader.cpp+6−7
@@ -366,10 +366,7 @@ namespace · 365 unmodified lines
366 366
367 if (parts.size() == 3)367 if (parts.size() == 3)
368 {368 {
369 item.size_in_bytes = std::strtol(parts[2].c_str(), nullptr, 10);369 item.size_in_bytes = std::stoi(parts[2]);
370 if (item.size_in_bytes == 0 && errno != 0)
371 throw std::runtime_error{ "Invalid adt size" };
372
373 item.id_table = read_id_table(r, tt, fc);370 item.id_table = read_id_table(r, tt, fc);
374 }371 }
375 372
@@ -429,9 +426,6 @@ namespace · 53 unmodified lines
429 {426 {
430 auto type_index = r.get_as_string_until('\n');427 auto type_index = r.get_as_string_until('\n');
431 const auto idx = std::stoi(type_index);428 const auto idx = std::stoi(type_index);
432 if (idx == 0)
433 throw std::runtime_error{ "Invalid type index" };
434
435 t.value_index_1 = type_table_t::types_mask | idx;429 t.value_index_1 = type_table_t::types_mask | idx;
436 break;430 break;
437 }431 }
@@ -681,6 +675,11 @@ namespace · 243 unmodified lines
681 return _file->header.module_name;675 return _file->header.module_name;
682 }676 }
683 677
678 size_t get_instruction_count() const
679 {
680 return _file->pc_table.size();
681 }
682
684 std::unique_ptr<symbol_debug_t> get_debug() override683 std::unique_ptr<symbol_debug_t> get_debug() override
685 {684 {
686 return std::make_unique<sbl_symbol_debug>(_file);685 return std::make_unique<sbl_symbol_debug>(_file);
src/exec/debugger.cpp+121−10
@@ -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 stop389 // 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
870std::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
859void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id)894void 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
1007void 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}
src/exec/exec.h+9−3
@@ -16,9 +16,10 @@ · 15 unmodified lines
16enum class debugger_options16enum class debugger_options
17{17{
18 none = 0,18 none = 0,
19 break_on_enter = 1,19 break_on_enter = (1 << 0),
20 break_on_module_load = 2,20 break_on_module_load = (1 << 1),
21 break_on_exception = 4,21 break_on_exception = (1 << 2),
22 disable_symbol_loading = (1 << 3),
22};23};
23 24
24DEFINE_ENUM_FLAG_OPERATORS(debugger_options);25DEFINE_ENUM_FLAG_OPERATORS(debugger_options);
@@ -30,6 +31,8 @@ class debugger final : public disvm::runtime::vm_tool_t · 5 unmodified lines
30public:31public:
31 debugger(const debugger_options);32 debugger(const debugger_options);
32 33
34 std::string resolve_function_source_line(std::shared_ptr<const disvm::runtime::vm_module_t> m, disvm::runtime::vm_pc_t pc);
35
33public: // vm_tool_t36public: // vm_tool_t
34 void on_load(disvm::runtime::vm_tool_controller_t &, std::size_t tool_id) override;37 void on_load(disvm::runtime::vm_tool_controller_t &, std::size_t tool_id) override;
35 38
@@ -40,9 +43,12 @@ public: · 4 unmodified lines
40 std::unordered_set<disvm::runtime::cookie_t> breakpoint_cookies;43 std::unordered_set<disvm::runtime::cookie_t> breakpoint_cookies;
41 44
42private:45private:
46 void load_symbols(const disvm::loaded_vm_module_t &);
47
43 debugger_options _options;48 debugger_options _options;
44 std::size_t _tool_id;49 std::size_t _tool_id;
45 std::unordered_set<disvm::runtime::cookie_t> _event_cookies;50 std::unordered_set<disvm::runtime::cookie_t> _event_cookies;
51 std::unordered_map<std::size_t, std::unique_ptr<disvm::symbol::symbol_debug_t>> _vm_id_to_symbol_debug;
46};52};
47 53
48#endif // _DISVM_SRC_EXEC_EXEC_H_54#endif // _DISVM_SRC_EXEC_EXEC_H_
src/include/disvm.h+2−2
@@ -39,7 +39,7 @@ namespace disvm · 38 unmodified lines
39 class loaded_vm_module_t final39 class loaded_vm_module_t final
40 {40 {
41 public:41 public:
42 std::size_t load_id;42 runtime::module_id_t vm_id;
43 43
44 // Origin of module44 // Origin of module
45 std::unique_ptr<runtime::vm_string_t> origin;45 std::unique_ptr<runtime::vm_string_t> origin;
@@ -89,7 +89,7 @@ namespace disvm · 43 unmodified lines
89 const runtime::vm_frame_t &initial_frame,89 const runtime::vm_frame_t &initial_frame,
90 runtime::vm_pc_t initial_pc);90 runtime::vm_pc_t initial_pc);
91 91
92 // Load the module at the supplied path into the virtual machine92 // Load the module at the supplied path into the virtual machine.
93 std::shared_ptr<runtime::vm_module_t> load_module(const char *path);93 std::shared_ptr<runtime::vm_module_t> load_module(const char *path);
94 94
95 // Enumerate all loaded modules - this can include references to modules that95 // Enumerate all loaded modules - this can include references to modules that
src/include/runtime.h+6−0
@@ -566,6 +566,8 @@ namespace disvm · 565 unmodified lines
566 using import_section_t = std::vector<import_vm_module_t>;566 using import_section_t = std::vector<import_vm_module_t>;
567 using handler_section_t = std::vector<handler_t>;567 using handler_section_t = std::vector<handler_t>;
568 568
569 using module_id_t = std::size_t;
570
569 // VM module571 // VM module
570 struct vm_module_t572 struct vm_module_t
571 {573 {
@@ -575,6 +577,10 @@ namespace disvm · 3 unmodified lines
575 577
576 ~vm_module_t();578 ~vm_module_t();
577 579
580 // This is a unique ID assigned to the module by the VM. It is associated with the path of the module and is idempotent
581 // with respect to the number of times the module is loaded. It is considered read-only.
582 module_id_t vm_id;
583
578 // Contains a magic number, digital signature, runtime flags, sizes of the other sections, and a description of the entry point584 // Contains a magic number, digital signature, runtime flags, sizes of the other sections, and a description of the entry point
579 header_t header;585 header_t header;
580 586
src/include/vm_asm.h+3−0
@@ -441,6 +441,9 @@ namespace disvm · 440 unmodified lines
441 // Get name of the module441 // Get name of the module
442 virtual const std::string& get_module_name() const = 0;442 virtual const std::string& get_module_name() const = 0;
443 443
444 // Get instruction count in the symbol data
445 virtual size_t get_instruction_count() const = 0;
446
444 // Get interface over symbol data targeted for debugging447 // Get interface over symbol data targeted for debugging
445 virtual std::unique_ptr<symbol_debug_t> get_debug() = 0;448 virtual std::unique_ptr<symbol_debug_t> get_debug() = 0;
446 };449 };
src/include/vm_tools.h+2−2
@@ -28,6 +28,7 @@ namespace disvm · 27 unmodified lines
28 const vm_thread_t *thread;28 const vm_thread_t *thread;
29 const vm_string_t *str;29 const vm_string_t *str;
30 const vm_alloc_t *alloc;30 const vm_alloc_t *alloc;
31 const loaded_vm_module_t *loaded_mod;
31 };32 };
32 33
33 // Event context34 // Event context
@@ -41,8 +42,7 @@ namespace disvm · 7 unmodified lines
41 // List of possible events42 // List of possible events
42 enum class vm_event_t43 enum class vm_event_t
43 {44 {
44 module_vm_load, // value1: std::shared_ptr<vm_module_t>45 module_vm_load, // value1: const loaded_vm_module_t
45 // value2: const vm_string_t - path on disk
46 46
47 module_thread_load, // value1: vm_registers_t47 module_thread_load, // value1: vm_registers_t
48 // value2: std::shared_ptr<vm_module_t>48 // value2: std::shared_ptr<vm_module_t>
src/vm/tool_dispatch.cpp+2−5
@@ -126,13 +126,10 @@ void vm_tool_dispatch_t::on_exception_unhandled(vm_registers_t &r, const vm_stri · 125 unmodified lines
126 _events.fire_event_callbacks(vm_event_t::exception_unhandled, cxt);126 _events.fire_event_callbacks(vm_event_t::exception_unhandled, cxt);
127}127}
128 128
129void vm_tool_dispatch_t::on_module_vm_load(std::shared_ptr<vm_module_t> &m, const vm_string_t &path)129void vm_tool_dispatch_t::on_module_vm_load(const loaded_vm_module_t &m)
130{130{
131 assert(m != nullptr);
132
133 vm_event_context_t cxt{};131 vm_event_context_t cxt{};
134 cxt.value1.module = &m;132 cxt.value1.loaded_mod = &m;
135 cxt.value2.str = &path;
136 133
137 _events.fire_event_callbacks(vm_event_t::module_vm_load, cxt);134 _events.fire_event_callbacks(vm_event_t::module_vm_load, cxt);
138}135}
src/vm/tool_dispatch.h+2−2
@@ -43,9 +43,9 @@ namespace disvm · 42 unmodified lines
43 void on_exception_unhandled(vm_registers_t &r, const vm_string_t &id, vm_alloc_t &e);43 void on_exception_unhandled(vm_registers_t &r, const vm_string_t &id, vm_alloc_t &e);
44 44
45 // Callback for when the VM loads a module from disk45 // Callback for when the VM loads a module from disk
46 void on_module_vm_load(std::shared_ptr<vm_module_t> &m, const vm_string_t &path);46 void on_module_vm_load(const loaded_vm_module_t &loaded_module);
47 47
48 // Callback for when a thread loads a module into its scope48 // Callback for when a thread loads a module into its scope.
49 void on_module_thread_load(vm_registers_t &r, std::shared_ptr<vm_module_t> &m);49 void on_module_thread_load(vm_registers_t &r, std::shared_ptr<vm_module_t> &m);
50 50
51 // Callback when thread lifetime begins.51 // Callback when thread lifetime begins.
src/vm/vm.cpp+7−4
@@ -134,6 +134,8 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 133 unmodified lines
134 if (module == nullptr)134 if (module == nullptr)
135 {135 {
136 module = std::shared_ptr<vm_module_t>{ std::move(read_module(path)) };136 module = std::shared_ptr<vm_module_t>{ std::move(read_module(path)) };
137 module->vm_id = iter->vm_id;
138
137 iter->module = module;139 iter->module = module;
138 140
139 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "reload: vm module: >>%s<<\n", path);141 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "reload: vm module: >>%s<<\n", path);
@@ -157,11 +159,12 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 17 unmodified lines
157 159
158 auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path));160 auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path));
159 161
160 auto load_id_next = std::size_t{ 0 };162 auto vm_id_next = std::size_t{ 1 };
161 if (!_modules.empty())163 if (!_modules.empty())
162 load_id_next = (_modules.front().load_id + 1);164 vm_id_next = (_modules.front().vm_id + 1);
163 165
164 _modules.push_front(std::move(loaded_vm_module_t{ load_id_next, std::move(path_local), new_module }));166 new_module->vm_id = vm_id_next;
167 _modules.push_front(std::move(loaded_vm_module_t{ vm_id_next, std::move(path_local), new_module }));
165 new_module_iter = _modules.begin();168 new_module_iter = _modules.begin();
166 }169 }
167 170
@@ -169,7 +172,7 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 1 unmodified line
169 {172 {
170 std::lock_guard<std::mutex> lock{ _tool_dispatch_lock };173 std::lock_guard<std::mutex> lock{ _tool_dispatch_lock };
171 if (_tool_dispatch != nullptr)174 if (_tool_dispatch != nullptr)
172 _tool_dispatch->on_module_vm_load(new_module, *new_module_iter->origin);175 _tool_dispatch->on_module_vm_load(*new_module_iter);
173 }176 }
174 177
175 if (debug::is_component_tracing_enabled<debug::component_trace_t::module>())178 if (debug::is_component_tracing_enabled<debug::component_trace_t::module>())