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
366366
367367 if (parts.size() == 3)
368368 {
369− item.size_in_bytes = std::strtol(parts[2].c_str(), nullptr, 10);
370− if (item.size_in_bytes == 0 && errno != 0)
371− throw std::runtime_error{ "Invalid adt size" };
372−
369+ item.size_in_bytes = std::stoi(parts[2]);
373370 item.id_table = read_id_table(r, tt, fc);
374371 }
375372
@@ -429,9 +426,6 @@ namespace · 53 unmodified lines
429426 {
430427 auto type_index = r.get_as_string_until('\n');
431428 const auto idx = std::stoi(type_index);
432− if (idx == 0)
433− throw std::runtime_error{ "Invalid type index" };
434−
435429 t.value_index_1 = type_table_t::types_mask | idx;
436430 break;
437431 }
@@ -681,6 +675,11 @@ namespace · 243 unmodified lines
681675 return _file->header.module_name;
682676 }
683677
678+ size_t get_instruction_count() const
679+ {
680+ return _file->pc_table.size();
681+ }
682+
684683 std::unique_ptr<symbol_debug_t> get_debug() override
685684 {
686685 return std::make_unique<sbl_symbol_debug>(_file);
src/exec/debugger.cpp+121−10
@@ -5,6 +5,7 @@ · 4 unmodified lines
55 //
66
77 #include <iostream>
8+#include <fstream>
89 #include <iomanip>
910 #include <sstream>
1011 #include <string>
@@ -117,16 +118,26 @@ namespace · 106 unmodified lines
117118 dbg_cmd_exec_t exec;
118119 };
119120
120− std::string stack_to_string(const vm_registers_t &r)
121+ std::string stack_to_string(const vm_registers_t &r, debugger &dbg)
121122 {
122123 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)
124125 {
125− auto module_name = "<No Name>";
126− if (module_ref.module->module_name != nullptr)
127− module_name = module_ref.module->module_name->str();
126+ auto resolved_name = dbg.resolve_function_source_line(module_ref.module, pc);
127+ if (!resolved_name.empty())
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+ }
128139
129− msg << module_name << "@" << pc << "\n";
140+ msg << "\n";
130141 return true;
131142 });
132143
@@ -215,7 +226,7 @@ namespace · 82 unmodified lines
215226 ss << "channel\n"
216227 << "\tbuffer size: " << c->get_buffer_size() << "\n";
217228
218− return ss;
229+ return ss;
219230 }
220231
221232 std::ostream& operator<<(std::ostream &ss, const vm_alloc_t *alloc)
@@ -330,7 +341,7 @@ namespace · 108 unmodified lines
330341 auto module_instance = m.module.lock();
331342 if (module_instance != nullptr)
332343 {
333− msg << std::setw(3) << m.load_id << ": "
344+ msg << std::setw(3) << m.vm_id << ": "
334345 << std::setw(16) << module_instance->module_name->str()
335346 << std::setw(0) << " - " << m.origin->str() << "\n";
336347 }
@@ -372,7 +383,7 @@ namespace · 35 unmodified lines
372383
373384 cxt.vm.enum_loaded_modules([module_id, &module](const loaded_vm_module_t &m)
374385 {
375− if (module_id != m.load_id)
386+ if (module_id != m.vm_id)
376387 return true;
377388
378389 // Found the matching ID so stop
@@ -500,7 +511,7 @@ namespace · 121 unmodified lines
500511 for (auto t : thread_registers)
501512 {
502513 msg << "Thread " << t.first << ":\n";
503− msg << stack_to_string(*t.second);
514+ msg << stack_to_string(*t.second, cxt.dbg);
504515 msg << "\n";
505516 }
506517
@@ -856,6 +867,30 @@ debugger::debugger(const debugger_options opt) · 349 unmodified lines
856867 , controller{ nullptr }
857868 { }
858869
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+
859894 void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id)
860895 {
861896 _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
909944 prompt(*this, *cxt.value1.registers);
910945 }));
911946
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+
912954 _event_cookies.emplace(controller->subscribe_event(vm_event_t::module_thread_load, [&](vm_event_t, vm_event_context_t &cxt)
913955 {
914956 auto m = (*cxt.value2.module);
@@ -960,4 +1002,73 @@ void debugger::on_unload() · 45 unmodified lines
9601002 controller->unsubscribe_event(ec);
9611003
9621004 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());
9631074 }
src/exec/exec.h+9−3
@@ -16,9 +16,10 @@ · 15 unmodified lines
1616 enum class debugger_options
1717 {
1818 none = 0,
19− break_on_enter = 1,
20− break_on_module_load = 2,
21− break_on_exception = 4,
19+ break_on_enter = (1 << 0),
20+ break_on_module_load = (1 << 1),
21+ break_on_exception = (1 << 2),
22+ disable_symbol_loading = (1 << 3),
2223 };
2324
2425 DEFINE_ENUM_FLAG_OPERATORS(debugger_options);
@@ -30,6 +31,8 @@ class debugger final : public disvm::runtime::vm_tool_t · 5 unmodified lines
3031 public:
3132 debugger(const debugger_options);
3233
34+ std::string resolve_function_source_line(std::shared_ptr<const disvm::runtime::vm_module_t> m, disvm::runtime::vm_pc_t pc);
35+
3336 public: // vm_tool_t
3437 void on_load(disvm::runtime::vm_tool_controller_t &, std::size_t tool_id) override;
3538
@@ -40,9 +43,12 @@ public: · 4 unmodified lines
4043 std::unordered_set<disvm::runtime::cookie_t> breakpoint_cookies;
4144
4245 private:
46+ void load_symbols(const disvm::loaded_vm_module_t &);
47+
4348 debugger_options _options;
4449 std::size_t _tool_id;
4550 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;
4652 };
4753
4854 #endif // _DISVM_SRC_EXEC_EXEC_H_
src/include/disvm.h+2−2
@@ -39,7 +39,7 @@ namespace disvm · 38 unmodified lines
3939 class loaded_vm_module_t final
4040 {
4141 public:
42− std::size_t load_id;
42+ runtime::module_id_t vm_id;
4343
4444 // Origin of module
4545 std::unique_ptr<runtime::vm_string_t> origin;
@@ -89,7 +89,7 @@ namespace disvm · 43 unmodified lines
8989 const runtime::vm_frame_t &initial_frame,
9090 runtime::vm_pc_t initial_pc);
9191
92− // Load the module at the supplied path into the virtual machine
92+ // Load the module at the supplied path into the virtual machine.
9393 std::shared_ptr<runtime::vm_module_t> load_module(const char *path);
9494
9595 // 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
566566 using import_section_t = std::vector<import_vm_module_t>;
567567 using handler_section_t = std::vector<handler_t>;
568568
569+ using module_id_t = std::size_t;
570+
569571 // VM module
570572 struct vm_module_t
571573 {
@@ -575,6 +577,10 @@ namespace disvm · 3 unmodified lines
575577
576578 ~vm_module_t();
577579
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+
578584 // Contains a magic number, digital signature, runtime flags, sizes of the other sections, and a description of the entry point
579585 header_t header;
580586
src/include/vm_asm.h+3−0
@@ -441,6 +441,9 @@ namespace disvm · 440 unmodified lines
441441 // Get name of the module
442442 virtual const std::string& get_module_name() const = 0;
443443
444+ // Get instruction count in the symbol data
445+ virtual size_t get_instruction_count() const = 0;
446+
444447 // Get interface over symbol data targeted for debugging
445448 virtual std::unique_ptr<symbol_debug_t> get_debug() = 0;
446449 };
src/include/vm_tools.h+2−2
@@ -28,6 +28,7 @@ namespace disvm · 27 unmodified lines
2828 const vm_thread_t *thread;
2929 const vm_string_t *str;
3030 const vm_alloc_t *alloc;
31+ const loaded_vm_module_t *loaded_mod;
3132 };
3233
3334 // Event context
@@ -41,8 +42,7 @@ namespace disvm · 7 unmodified lines
4142 // List of possible events
4243 enum class vm_event_t
4344 {
44− module_vm_load, // value1: std::shared_ptr<vm_module_t>
45− // value2: const vm_string_t - path on disk
45+ module_vm_load, // value1: const loaded_vm_module_t
4646
4747 module_thread_load, // value1: vm_registers_t
4848 // 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
126126 _events.fire_event_callbacks(vm_event_t::exception_unhandled, cxt);
127127 }
128128
129−void vm_tool_dispatch_t::on_module_vm_load(std::shared_ptr<vm_module_t> &m, const vm_string_t &path)
129+void vm_tool_dispatch_t::on_module_vm_load(const loaded_vm_module_t &m)
130130 {
131− assert(m != nullptr);
132−
133131 vm_event_context_t cxt{};
134− cxt.value1.module = &m;
135− cxt.value2.str = &path;
132+ cxt.value1.loaded_mod = &m;
136133
137134 _events.fire_event_callbacks(vm_event_t::module_vm_load, cxt);
138135 }
src/vm/tool_dispatch.h+2−2
@@ -43,9 +43,9 @@ namespace disvm · 42 unmodified lines
4343 void on_exception_unhandled(vm_registers_t &r, const vm_string_t &id, vm_alloc_t &e);
4444
4545 // 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);
4747
48− // Callback for when a thread loads a module into its scope
48+ // Callback for when a thread loads a module into its scope.
4949 void on_module_thread_load(vm_registers_t &r, std::shared_ptr<vm_module_t> &m);
5050
5151 // 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
134134 if (module == nullptr)
135135 {
136136 module = std::shared_ptr<vm_module_t>{ std::move(read_module(path)) };
137+ module->vm_id = iter->vm_id;
138+
137139 iter->module = module;
138140
139141 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
157159
158160 auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path));
159161
160− auto load_id_next = std::size_t{ 0 };
162+ auto vm_id_next = std::size_t{ 1 };
161163 if (!_modules.empty())
162− load_id_next = (_modules.front().load_id + 1);
164+ vm_id_next = (_modules.front().vm_id + 1);
163165
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 }));
165168 new_module_iter = _modules.begin();
166169 }
167170
@@ -169,7 +172,7 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 1 unmodified line
169172 {
170173 std::lock_guard<std::mutex> lock{ _tool_dispatch_lock };
171174 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);
173176 }
174177
175178 if (debug::is_component_tracing_enabled<debug::component_trace_t::module>())