Showing the changes to src/exec/debugger.cpp (1 of 4 files). Show all
Unified · Split
| @@ -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 '" |