| @@ -123,10 +123,11 @@ namespace · 122 unmodified lines |
| 123 | auto msg = std::stringstream{}; | 123 | auto msg = std::stringstream{}; |
| 124 | walk_stack(r, [&msg, &dbg](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) |
| 125 | { | 125 | { |
| 126 | auto resolved_name = dbg.resolve_function_source_line(module_ref.module, pc); | 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 (!resolved_name.empty()) | 127 | if (!func_maybe.empty()) |
| 128 | { | 128 | { |
| 129 | msg << resolved_name; | 129 | assert(func_maybe.size() == 1); |
| | 130 | msg << module_ref.module->module_name->str() << "!" << func_maybe[0]; |
| 130 | } | 131 | } |
| 131 | else | 132 | else |
| 132 | { | 133 | { |
| @@ -336,14 +337,19 @@ namespace · 203 unmodified lines |
| 336 | void cmd_print_modules(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) | 337 | void cmd_print_modules(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) |
| 337 | { | 338 | { |
| 338 | auto msg = std::stringstream{}; | 339 | 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) |
| 340 | { | 341 | { |
| 341 | auto module_instance = m.module.lock(); | 342 | auto module_instance = m.module.lock(); |
| 342 | if (module_instance != nullptr) | 343 | if (module_instance != nullptr) |
| 343 | { | 344 | { |
| 344 | msg << std::setw(3) << m.vm_id << ": " | 345 | msg << std::setw(3) << m.vm_id << ": " |
| 345 | << std::setw(16) << module_instance->module_name->str() | 346 | << 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"; |
| 347 | } | 353 | } |
| 348 | | 354 | |
| 349 | return true; | 355 | return true; |
| @@ -352,6 +358,38 @@ namespace · 2 unmodified lines |
| 352 | debug_print_info(msg.str()); | 358 | debug_print_info(msg.str()); |
| 353 | } | 359 | } |
| 354 | | 360 | |
| | 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 | |
| 355 | void cmd_breakpoint_set(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) | 393 | void cmd_breakpoint_set(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) |
| 356 | { | 394 | { |
| 357 | if (a.size() < 2) | 395 | if (a.size() < 2) |
| @@ -456,7 +494,20 @@ namespace · 98 unmodified lines |
| 456 | const auto details = cxt.dbg.controller->get_breakpoint_details(cookie); | 494 | const auto details = cxt.dbg.controller->get_breakpoint_details(cookie); |
| 457 | msg << std::setw(3) << cookie << ": " | 495 | msg << std::setw(3) << cookie << ": " |
| 458 | << std::setw(16) << details.module->module_name->str() | 496 | << 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"; |
| 460 | } | 511 | } |
| 461 | | 512 | |
| 462 | debug_print_info(msg.str()); | 513 | debug_print_info(msg.str()); |
| @@ -567,21 +618,24 @@ namespace · 104 unmodified lines |
| 567 | const auto &r = *cxt.current_register; | 618 | const auto &r = *cxt.current_register; |
| 568 | const auto &code_section = r.module_ref->code_section; | 619 | const auto &code_section = r.module_ref->code_section; |
| 569 | | 620 | |
| 570 | auto begin_index = static_cast<std::size_t>(r.pc); | 621 | auto begin_pc = static_cast<std::size_t>(r.pc); |
| 571 | auto end_index = static_cast<std::size_t>(r.pc + 1); | 622 | auto end_pc = static_cast<std::size_t>(r.pc); |
| 572 | if (disassemble_length < 0) | 623 | if (disassemble_length < 0) |
| 573 | { | 624 | { |
| 574 | const auto begin_maybe = r.pc - (-disassemble_length) + 1; | 625 | 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)); |
| 576 | } | 627 | } |
| 577 | else | 628 | else |
| 578 | { | 629 | { |
| 579 | const auto end_maybe = static_cast<std::size_t>(r.pc + disassemble_length); | 630 | 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()); |
| 581 | } | 632 | } |
| 582 | | 633 | |
| | 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 | |
| 583 | auto msg = std::stringstream{}; | 637 | 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) |
| 585 | { | 639 | { |
| 586 | auto op = code_section[i].op; | 640 | auto op = code_section[i].op; |
| 587 | | 641 | |
| @@ -603,6 +657,14 @@ namespace · 15 unmodified lines |
| 603 | } | 657 | } |
| 604 | } | 658 | } |
| 605 | | 659 | |
| | 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 | |
| 606 | msg << op_prefix << std::setw(5) << i << ": " << op << "\n"; | 668 | msg << op_prefix << std::setw(5) << i << ": " << op << "\n"; |
| 607 | } | 669 | } |
| 608 | | 670 | |
| @@ -771,6 +833,7 @@ namespace · 162 unmodified lines |
| 771 | { "r", "Print registers", nullptr, nullptr, cmd_print_registers }, | 833 | { "r", "Print registers", nullptr, nullptr, cmd_print_registers }, |
| 772 | { "t", "Print all threads", nullptr, nullptr, cmd_print_threads }, | 834 | { "t", "Print all threads", nullptr, nullptr, cmd_print_threads }, |
| 773 | { "m", "Print all loaded modules", nullptr, nullptr, cmd_print_modules }, | 835 | { "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 }, |
| 774 | | 837 | |
| 775 | { "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 }, | 838 | { "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 }, |
| 776 | { "bc", "Clear breakpoint(s)", "bc ([0-9]+|\\*)", "bc 3, bc *", cmd_breakpoint_clear }, | 839 | { "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 |
| 883 | , controller{ nullptr } | 946 | , controller{ nullptr } |
| 884 | { } | 947 | { } |
| 885 | | 948 | |
| 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 |
| 887 | { | 954 | { |
| 888 | if (m == nullptr) | 955 | auto iter = _vm_id_to_symbol_info.find(vm_id); |
| 889 | return{}; | 956 | if (iter == std::cend(_vm_id_to_symbol_info)) |
| 890 | | | |
| 891 | auto iter = _vm_id_to_symbol_debug.find(m->vm_id); | | |
| 892 | if (iter == std::end(_vm_id_to_symbol_debug)) | | |
| 893 | return{}; | 957 | return{}; |
| 894 | | 958 | |
| 895 | std::stringstream ss; | 959 | auto &d = *(iter->second.iter); |
| 896 | auto &d = *(iter->second); | | |
| 897 | | 960 | |
| 898 | // Position the debug symbol iterator | 961 | // 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()); |
| 900 | | 982 | |
| 901 | const auto src = d.current_source_location(); | 983 | } while (d.try_advance_pc(symbol::advance_pc_t::next_debug_statement, &next_pc)); |
| 902 | | 984 | |
| 903 | ss << m->module_name->str() | 985 | return resolved_functions_by_pc; |
| 904 | << '!' | 986 | } |
| 905 | << d.current_function_name(symbol::function_name_format_t::declaration) | 987 | |
| 906 | << " at " | 988 | std::vector<std::string> debugger::get_module_functions(disvm::runtime::module_id_t vm_id, const char *match) const |
| 907 | << d.get_source_by_id(src.source_id) | 989 | { |
| 908 | << ':' | 990 | std::vector<std::string> funcs; |
| 909 | << src.begin_line; | 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 | << "]"; |
| 910 | | 1009 | |
| 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); |
| 912 | } | 1020 | } |
| 913 | | 1021 | |
| 914 | void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id) | 1022 | 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 |
| 1081 | } | 1189 | } |
| 1082 | | 1190 | |
| 1083 | const auto vm_id = loaded_mod.vm_id; | 1191 | 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"); | 1192 | assert(_vm_id_to_symbol_info.find(vm_id) == std::end(_vm_id_to_symbol_info) && "Module VM ID should be unique"); |
| 1085 | _vm_id_to_symbol_debug[vm_id] = symbol_data->get_debug(); | 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) }; |
| 1086 | | 1195 | |
| 1087 | std::stringstream msg; | 1196 | std::stringstream msg; |
| 1088 | msg << "Symbol file '" | 1197 | msg << "Symbol file '" |