History

Add support for inleaving source information into disassembly view in debugger.

arr committed 9 years ago

Commit eb8b0901 parent 2b710fc4 files · +217 −59Browse files

Unified · Split

4 files
  1. src/asm/sbl_reader.cpp +28 −14
  2. src/exec/debugger.cpp +140 −31
  3. src/exec/exec.h +24 −2
  4. src/include/vm_asm.h +25 −12
src/asm/sbl_reader.cpp+28−14
@@ -598,7 +598,7 @@ namespace · 597 unmodified lines
598598 return "poly<T>";
599599
600600 case type_class_t::array:
601− return to_string(tt.resolve_table_index<type_t>(t.value_index_1), tt) + "[]";
601+ return "array of " + to_string(tt.resolve_table_index<type_t>(t.value_index_1), tt);
602602 case type_class_t::channel:
603603 return "chan of " + to_string(tt.resolve_table_index<type_t>(t.value_index_1), tt);
604604 case type_class_t::list:
@@ -656,13 +656,13 @@ namespace · 51 unmodified lines
656656 {
657657 std::stringstream ss;
658658
659− if (util::has_flag(fmt, function_name_format_t::name))
659+ const auto inc_name = util::has_flag(fmt, function_name_format_t::name);
660+ if (inc_name)
660661 ss << f.name;
661662
662− auto has_argument = false;
663− if (util::has_flag(fmt, function_name_format_t::arguments))
663+ const auto inc_arguments = util::has_flag(fmt, function_name_format_t::arguments);
664+ if (inc_arguments)
664665 {
665− has_argument = true;
666666 ss << '(';
667667
668668 auto first = true;
@@ -678,9 +678,11 @@ namespace · 9 unmodified lines
678678 ss << ')';
679679 }
680680
681− if (util::has_flag(fmt, function_name_format_t::return_type))
681+ // Only add the return value if it is not 'none'
682+ if (util::has_flag(fmt, function_name_format_t::return_type)
683+ && f.return_type.type_class != type_class_t::none)
682684 {
683− if (has_argument)
685+ if (inc_name || inc_arguments)
684686 ss << ':';
685687
686688 ss << to_string(f.return_type, tt);
@@ -689,17 +691,17 @@ namespace · 2 unmodified lines
689691 return ss.str();
690692 }
691693
692− class sbl_symbol_debug final : public symbol_debug_t
694+ class sbl_symbol_pc_iter final : public symbol_pc_iter_t
693695 {
694696 public:
695− sbl_symbol_debug(std::shared_ptr<sbl_file_t> f)
697+ sbl_symbol_pc_iter(std::shared_ptr<const sbl_file_t> f)
696698 : _current_pc{ 0 }
697699 , _file{ f }
698700 {
699701 assert(_file != nullptr);
700702 }
701703
702− public: // symbol_debug_t
704+ public: // symbol_pc_iter_t
703705 void set_current_pc(disvm::runtime::vm_pc_t pc) override
704706 {
705707 assert(0 <= pc && pc < static_cast<int32_t>(_file->pc_table.size()));
@@ -784,7 +786,7 @@ namespace · 78 unmodified lines
784786
785787 private:
786788 runtime::vm_pc_t _current_pc;
787− std::shared_ptr<sbl_file_t> _file;
789+ std::shared_ptr<const sbl_file_t> _file;
788790 };
789791
790792 class sbl_symbol_data final : public symbol_data_t
@@ -807,13 +809,25 @@ namespace · 16 unmodified lines
807809 return _file->pc_table.size();
808810 }
809811
810− std::unique_ptr<symbol_debug_t> get_debug() override
812+ std::vector<function_data_t> get_functions(function_name_format_t f) const override
811813 {
812− return std::make_unique<sbl_symbol_debug>(_file);
814+ std::vector<function_data_t> funcs;
815+ for (func_table_t::const_reference func : _file->func_table)
816+ {
817+ auto formatted_name = to_string(func, _file->type_table, f);
818+ funcs.push_back({ func.entry_pc, func.limit_pc, std::move(formatted_name) });
819+ }
820+
821+ return funcs;
822+ }
823+
824+ std::unique_ptr<symbol_pc_iter_t> get_pc_iter() const override
825+ {
826+ return std::make_unique<sbl_symbol_pc_iter>(_file);
813827 }
814828
815829 private:
816− std::shared_ptr<sbl_file_t> _file;
830+ std::shared_ptr<const sbl_file_t> _file;
817831 };
818832 }
819833
src/exec/debugger.cpp+140−31
@@ -123,10 +123,11 @@ namespace · 122 unmodified lines
123123 auto msg = std::stringstream{};
124124 walk_stack(r, [&msg, &dbg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref)
125125 {
126− auto resolved_name = dbg.resolve_function_source_line(module_ref.module, pc);
127− if (!resolved_name.empty())
126+ auto func_maybe = dbg.resolve_to_function_source_line(module_ref.module->vm_id, pc, pc, symbol::function_name_format_t::name);
127+ if (!func_maybe.empty())
128128 {
129− msg << resolved_name;
129+ assert(func_maybe.size() == 1);
130+ msg << module_ref.module->module_name->str() << "!" << func_maybe[0];
130131 }
131132 else
132133 {
@@ -336,14 +337,19 @@ namespace · 203 unmodified lines
336337 void cmd_print_modules(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt)
337338 {
338339 auto msg = std::stringstream{};
339− cxt.vm.enum_loaded_modules([&msg](const loaded_vm_module_t &m)
340+ cxt.vm.enum_loaded_modules([&msg, &cxt](const loaded_vm_module_t &m)
340341 {
341342 auto module_instance = m.module.lock();
342343 if (module_instance != nullptr)
343344 {
344345 msg << std::setw(3) << m.vm_id << ": "
345346 << std::setw(16) << module_instance->module_name->str()
346− << std::setw(0) << " - " << m.origin->str() << "\n";
347+ << std::setw(0) << " - " << m.origin->str();
348+
349+ if (cxt.dbg.symbols_exist(m.vm_id))
350+ msg << " [symbols loaded]";
351+
352+ msg << "\n";
347353 }
348354
349355 return true;
@@ -352,6 +358,38 @@ namespace · 2 unmodified lines
352358 debug_print_info(msg.str());
353359 }
354360
361+ void cmd_print_symbols(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt)
362+ {
363+ if (a.size() < 2)
364+ throw debug_cmd_error_t{ "Must supply a module ID" };
365+
366+ module_id_t vm_id;
367+ try
368+ {
369+ vm_id = std::stoul(a[1]);
370+ }
371+ catch (...)
372+ {
373+ throw debug_cmd_error_t{ "Invalid module ID format" };
374+ }
375+
376+ if (!cxt.dbg.symbols_exist(vm_id))
377+ throw debug_cmd_error_t{ "No symbols loaded for module" };
378+
379+ // Interpret the last non-module ID value as the partial name to match.
380+ const char *name_to_match = nullptr;
381+ if (a.size() > 2)
382+ name_to_match = a.back().c_str();
383+
384+ auto msg = std::stringstream{};
385+ auto module_funcs = cxt.dbg.get_module_functions(vm_id, name_to_match);
386+ msg << "Function name [PC range]\n";
387+ for (auto &f : module_funcs)
388+ msg << " " << f << "\n";
389+
390+ debug_print_info(msg.str());
391+ }
392+
355393 void cmd_breakpoint_set(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt)
356394 {
357395 if (a.size() < 2)
@@ -456,7 +494,20 @@ namespace · 98 unmodified lines
456494 const auto details = cxt.dbg.controller->get_breakpoint_details(cookie);
457495 msg << std::setw(3) << cookie << ": "
458496 << std::setw(16) << details.module->module_name->str()
459− << std::setw(0) << "@" << details.pc << "\n";
497+ << std::setw(0);
498+
499+ auto func_maybe = cxt.dbg.resolve_to_function_source_line(details.module->vm_id, details.pc, details.pc, symbol::function_name_format_t::name);
500+ if (!func_maybe.empty())
501+ {
502+ assert(func_maybe.size() == 1);
503+ msg << "!" << func_maybe[0];
504+ }
505+ else
506+ {
507+ msg << "@" << details.pc;
508+ }
509+
510+ msg << "\n";
460511 }
461512
462513 debug_print_info(msg.str());
@@ -567,21 +618,24 @@ namespace · 104 unmodified lines
567618 const auto &r = *cxt.current_register;
568619 const auto &code_section = r.module_ref->code_section;
569620
570− auto begin_index = static_cast<std::size_t>(r.pc);
571− auto end_index = static_cast<std::size_t>(r.pc + 1);
621+ auto begin_pc = static_cast<std::size_t>(r.pc);
622+ auto end_pc = static_cast<std::size_t>(r.pc);
572623 if (disassemble_length < 0)
573624 {
574625 const auto begin_maybe = r.pc - (-disassemble_length) + 1;
575− begin_index = static_cast<std::size_t>(std::max(begin_maybe, 0));
626+ begin_pc= static_cast<std::size_t>(std::max(begin_maybe, 0));
576627 }
577628 else
578629 {
579630 const auto end_maybe = static_cast<std::size_t>(r.pc + disassemble_length);
580− end_index = std::min(end_maybe, code_section.size());
631+ end_pc = std::min(end_maybe, code_section.size());
581632 }
582633
634+ auto resolved_pc = cxt.dbg.resolve_to_function_source_line(r.module_ref->module->vm_id, begin_pc, end_pc, symbol::function_name_format_t::name);
635+ assert(resolved_pc.empty() || resolved_pc.size() == (end_pc - begin_pc + 1) && "Resolved PCs should have failed or match the PC count");
636+
583637 auto msg = std::stringstream{};
584− for (auto i = begin_index; i < end_index; ++i)
638+ for (auto i = begin_pc; i < (end_pc + 1); ++i)
585639 {
586640 auto op = code_section[i].op;
587641
@@ -603,6 +657,14 @@ namespace · 15 unmodified lines
603657 }
604658 }
605659
660+ // Print source location if resolved
661+ if (!resolved_pc.empty())
662+ {
663+ const auto & r = resolved_pc[i - begin_pc];
664+ if (!r.empty())
665+ msg << r << "\n";
666+ }
667+
606668 msg << op_prefix << std::setw(5) << i << ": " << op << "\n";
607669 }
608670
@@ -771,6 +833,7 @@ namespace · 162 unmodified lines
771833 { "r", "Print registers", nullptr, nullptr, cmd_print_registers },
772834 { "t", "Print all threads", nullptr, nullptr, cmd_print_threads },
773835 { "m", "Print all loaded modules", nullptr, nullptr, cmd_print_modules },
836+ { "sym", "Print symbols for the supplied module", "sym [0-9]+ <partial function name>?", "sym 2 foo (Print symbols in module 2 containing 'foo')", cmd_print_symbols },
774837
775838 { "bs", "Set breakpoint at the specified IP in a loaded module", "bs [0-9]+ [0-9]*", "bs 3 5 (Breakpoint at IP 3 in module 5), bs 4 (Breakpoint at IP 4 in current module)", cmd_breakpoint_set },
776839 { "bc", "Clear breakpoint(s)", "bc ([0-9]+|\\*)", "bc 3, bc *", cmd_breakpoint_clear },
@@ -883,32 +946,77 @@ debugger::debugger(const debugger_options opt) · 106 unmodified lines
883946 , controller{ nullptr }
884947 { }
885948
886−std::string debugger::resolve_function_source_line(std::shared_ptr<const vm_module_t> m, vm_pc_t pc)
949+std::vector<std::string> debugger::resolve_to_function_source_line(
950+ disvm::runtime::module_id_t vm_id,
951+ vm_pc_t begin_pc,
952+ vm_pc_t end_pc,
953+ symbol::function_name_format_t fmt) const
887954 {
888− if (m == nullptr)
889− return{};
890−
891− auto iter = _vm_id_to_symbol_debug.find(m->vm_id);
892− if (iter == std::end(_vm_id_to_symbol_debug))
955+ auto iter = _vm_id_to_symbol_info.find(vm_id);
956+ if (iter == std::cend(_vm_id_to_symbol_info))
893957 return{};
894958
895− std::stringstream ss;
896− auto &d = *(iter->second);
959+ auto &d = *(iter->second.iter);
897960
898961 // Position the debug symbol iterator
899− d.set_current_pc(pc);
962+ d.set_current_pc(begin_pc);
963+
964+ std::vector<std::string> resolved_functions_by_pc(end_pc - begin_pc + 1);
965+ vm_pc_t next_pc = begin_pc;
966+ do
967+ {
968+ if (next_pc > end_pc)
969+ break;
970+
971+ std::stringstream ss;
972+ const auto function_name = d.current_function_name(fmt);
973+ if (!function_name.empty())
974+ ss << function_name << " at ";
975+
976+ const auto source_loc = d.current_source_location();
977+ ss << d.get_source_by_id(source_loc.source_id)
978+ << ':'
979+ << source_loc.begin_line;
980+
981+ resolved_functions_by_pc[next_pc - begin_pc] = std::move(ss.str());
900982
901− const auto src = d.current_source_location();
983+ } while (d.try_advance_pc(symbol::advance_pc_t::next_debug_statement, &next_pc));
902984
903− ss << m->module_name->str()
904− << '!'
905− << d.current_function_name(symbol::function_name_format_t::declaration)
906− << " at "
907− << d.get_source_by_id(src.source_id)
908− << ':'
909− << src.begin_line;
985+ return resolved_functions_by_pc;
986+}
987+
988+std::vector<std::string> debugger::get_module_functions(disvm::runtime::module_id_t vm_id, const char *match) const
989+{
990+ std::vector<std::string> funcs;
991+ auto iter = _vm_id_to_symbol_info.find(vm_id);
992+ if (iter != std::cend(_vm_id_to_symbol_info))
993+ {
994+ auto f_infos = iter->second.data->get_functions(symbol::function_name_format_t::declaration);
995+ for (auto &fi : f_infos)
996+ {
997+ // Only return functions that match the supplied string
998+ if (match != nullptr
999+ && fi.name.find(match) == std::string::npos)
1000+ continue;
1001+
1002+ std::stringstream ss;
1003+ ss << fi.name
1004+ << " ["
1005+ << fi.entry_pc
1006+ << ", "
1007+ << fi.limit_pc
1008+ << "]";
9101009
911− return ss.str();
1010+ funcs.push_back(ss.str());
1011+ }
1012+ }
1013+
1014+ return funcs;
1015+}
1016+
1017+bool debugger::symbols_exist(disvm::runtime::module_id_t vm_id) const
1018+{
1019+ return _vm_id_to_symbol_info.find(vm_id) != std::cend(_vm_id_to_symbol_info);
9121020 }
9131021
9141022 void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id)
@@ -1081,8 +1189,9 @@ void debugger::load_symbols(const disvm::loaded_vm_module_t &loaded_mod) · 166 unmodified lines
10811189 }
10821190
10831191 const auto vm_id = loaded_mod.vm_id;
1084− assert(_vm_id_to_symbol_debug.find(vm_id) == std::end(_vm_id_to_symbol_debug) && "Module VM ID should be unique");
1085− _vm_id_to_symbol_debug[vm_id] = symbol_data->get_debug();
1192+ assert(_vm_id_to_symbol_info.find(vm_id) == std::end(_vm_id_to_symbol_info) && "Module VM ID should be unique");
1193+ auto symbol_iter = symbol_data->get_pc_iter();
1194+ _vm_id_to_symbol_info[vm_id] = { std::move(symbol_data), std::move(symbol_iter) };
10861195
10871196 std::stringstream msg;
10881197 msg << "Symbol file '"
src/exec/exec.h+24−2
@@ -9,6 +9,9 @@ · 8 unmodified lines
99
1010 #include <cstdint>
1111 #include <unordered_set>
12+#include <vector>
13+#include <string>
14+#include <memory>
1215 #include <disvm.h>
1316 #include <vm_tools.h>
1417 #include <utils.h>
@@ -31,7 +34,19 @@ class debugger final : public disvm::runtime::vm_tool_t · 16 unmodified lines
3134 public:
3235 debugger(const debugger_options);
3336
34− std::string resolve_function_source_line(std::shared_ptr<const disvm::runtime::vm_module_t> m, disvm::runtime::vm_pc_t pc);
37+ // Resolve all PCs in the range from begin and end (inclusive)
38+ std::vector<std::string> resolve_to_function_source_line(
39+ disvm::runtime::module_id_t vm_id,
40+ disvm::runtime::vm_pc_t begin_pc,
41+ disvm::runtime::vm_pc_t end_pc,
42+ disvm::symbol::function_name_format_t fmt) const;
43+
44+ // Return all functions for the supplied module ID.
45+ // Optionally match a function against the supplied string.
46+ std::vector<std::string> get_module_functions(disvm::runtime::module_id_t vm_id, const char *match) const;
47+
48+ // Check if the supplied module ID has symbols loaded
49+ bool symbols_exist(disvm::runtime::module_id_t vm_id) const;
3550
3651 public: // vm_tool_t
3752 void on_load(disvm::runtime::vm_tool_controller_t &, std::size_t tool_id) override;
@@ -48,7 +63,14 @@ private: · 10 unmodified lines
4863 debugger_options _options;
4964 std::size_t _tool_id;
5065 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;
66+
67+ struct symbol_info_t
68+ {
69+ std::unique_ptr<disvm::symbol::symbol_data_t> data;
70+ std::unique_ptr<disvm::symbol::symbol_pc_iter_t> iter;
71+ };
72+
73+ std::unordered_map<std::size_t, symbol_info_t> _vm_id_to_symbol_info;
5274 };
5375
5476 #endif // _DISVM_SRC_EXEC_EXEC_H_
src/include/vm_asm.h+25−12
@@ -407,6 +407,7 @@ namespace disvm · 406 unmodified lines
407407 int32_t end_column;
408408 };
409409
410+ // Enumeration for formatting a function name
410411 enum class function_name_format_t
411412 {
412413 name = 1,
@@ -418,20 +419,20 @@ namespace disvm · 5 unmodified lines
418419
419420 DEFINE_ENUM_FLAG_OPERATORS(function_name_format_t);
420421
421− class symbol_debug_t
422+ // Enumeration for advancing the pc
423+ enum class advance_pc_t
424+ {
425+ current, // Return the current pc
426+ next_pc,
427+ next_debug_statement, // Advance to the next debug statement (i.e. source language statement)
428+ };
429+
430+ class symbol_pc_iter_t
422431 {
423432 public:
424433 // Set the current pc
425434 virtual void set_current_pc(disvm::runtime::vm_pc_t) = 0;
426435
427− // Enumeration for advancing the pc
428− enum class advance_pc_t
429− {
430− current, // Return the current pc
431− next_pc,
432− next_debug_statement, // Advance to the next debug statement (i.e. source language statement)
433− };
434−
435436 // Advance the current pc.
436437 // Returns 'true' if the function was successful, otherwise 'false'.
437438 virtual bool try_advance_pc(advance_pc_t, disvm::runtime::vm_pc_t *pc_after_advance = nullptr) = 0;
@@ -439,7 +440,7 @@ namespace disvm · 1 unmodified line
439440 // Return the name of the function containing the current pc.
440441 // Optionally format the returned string.
441442 // This function will throw a 'std::runtime_error' if a function doesn't exist at the current pc.
442− virtual std::string current_function_name(function_name_format_t f = function_name_format_t::name) const = 0;
443+ virtual std::string current_function_name(function_name_format_t f) const = 0;
443444
444445 // Return the source location for the current pc
445446 virtual source_ref_t current_source_location() const = 0;
@@ -448,6 +449,13 @@ namespace disvm · 2 unmodified lines
448449 virtual const std::string &get_source_by_id(int32_t source_id) const = 0;
449450 };
450451
452+ struct function_data_t
453+ {
454+ disvm::runtime::vm_pc_t entry_pc;
455+ disvm::runtime::vm_pc_t limit_pc;
456+ std::string name;
457+ };
458+
451459 class symbol_data_t
452460 {
453461 public:
@@ -457,10 +465,15 @@ namespace disvm · 3 unmodified lines
457465 // Get instruction count in the symbol data
458466 virtual size_t get_instruction_count() const = 0;
459467
460− // Get interface over symbol data targeted for debugging
461− virtual std::unique_ptr<symbol_debug_t> get_debug() = 0;
468+ // Get list of functions in module.
469+ // Supply a formatting enum for the function name.
470+ virtual std::vector<function_data_t> get_functions(function_name_format_t f) const = 0;
471+
472+ // Get pc iterator interface over symbol data
473+ virtual std::unique_ptr<symbol_pc_iter_t> get_pc_iter() const = 0;
462474 };
463475
476+ // Read in symbol data from the supplied stream
464477 std::unique_ptr<symbol_data_t> read(std::istream &);
465478 }
466479