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 | 123 | auto msg = std::stringstream{}; |
| 124 | 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); | |
| 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()) | |
| 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 | 132 | else |
| 132 | 133 | { |
| @@ -336,14 +337,19 @@ namespace · 203 unmodified lines | ||
| 336 | 337 | void cmd_print_modules(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) |
| 337 | 338 | { |
| 338 | 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 | 342 | auto module_instance = m.module.lock(); |
| 342 | 343 | if (module_instance != nullptr) |
| 343 | 344 | { |
| 344 | 345 | msg << std::setw(3) << m.vm_id << ": " |
| 345 | 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 | 355 | return true; |
| @@ -352,6 +358,38 @@ namespace · 2 unmodified lines | ||
| 352 | 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 | 393 | void cmd_breakpoint_set(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) |
| 356 | 394 | { |
| 357 | 395 | if (a.size() < 2) |
| @@ -456,7 +494,20 @@ namespace · 98 unmodified lines | ||
| 456 | 494 | const auto details = cxt.dbg.controller->get_breakpoint_details(cookie); |
| 457 | 495 | msg << std::setw(3) << cookie << ": " |
| 458 | 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 | 513 | debug_print_info(msg.str()); |
| @@ -567,21 +618,24 @@ namespace · 104 unmodified lines | ||
| 567 | 618 | const auto &r = *cxt.current_register; |
| 568 | 619 | const auto &code_section = r.module_ref->code_section; |
| 569 | 620 | |
| 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); | |
| 572 | 623 | if (disassemble_length < 0) |
| 573 | 624 | { |
| 574 | 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 | 628 | else |
| 578 | 629 | { |
| 579 | 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 | 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 | 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 | 668 | msg << op_prefix << std::setw(5) << i << ": " << op << "\n"; |
| 607 | 669 | } |
| 608 | 670 | |
| @@ -771,6 +833,7 @@ namespace · 162 unmodified lines | ||
| 771 | 833 | { "r", "Print registers", nullptr, nullptr, cmd_print_registers }, |
| 772 | 834 | { "t", "Print all threads", nullptr, nullptr, cmd_print_threads }, |
| 773 | 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 | 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 | 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 | 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) | |
| 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)) | |
| 893 | 957 | return{}; |
| 894 | 958 | |
| 895 | − std::stringstream ss; | |
| 896 | − auto &d = *(iter->second); | |
| 959 | + auto &d = *(iter->second.iter); | |
| 897 | 960 | |
| 898 | 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() | |
| 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 | + << "]"; | |
| 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 | 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 | 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"); | |
| 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) }; | |
| 1086 | 1195 | |
| 1087 | 1196 | std::stringstream msg; |
| 1088 | 1197 | msg << "Symbol file '" |