Unified · Split
| @@ -9,9 +9,7 @@ · 8 unmodified lines | |||
| 9 | #include <sstream> | 9 | #include <sstream> |
| 10 | #include <string> | 10 | #include <string> |
| 11 | #include <algorithm> | 11 | #include <algorithm> |
| 12 | #include <locale> | ||
| 13 | #include <map> | 12 | #include <map> |
| 14 | #include <vm_tools.h> | ||
| 15 | #include <utils.h> | 13 | #include <utils.h> |
| 16 | #include <vm_memory.h> | 14 | #include <vm_memory.h> |
| 17 | #include <vm_asm.h> | 15 | #include <vm_asm.h> |
| @@ -76,16 +74,18 @@ namespace · 58 unmodified lines | |||
| 76 | // Debug command context | 74 | // Debug command context |
| 77 | struct dbg_cmd_cxt_t | 75 | struct dbg_cmd_cxt_t |
| 78 | { | 76 | { |
| 79 | dbg_cmd_cxt_t(const vm_t &v, const vm_registers_t &r) | 77 | dbg_cmd_cxt_t(debugger &d, const vm_registers_t &r) |
| 80 | : initial_register{ r } | 78 | : dbg{ d } |
| 81 | , exit_break{ false } | 79 | , exit_break{ false } |
| 82 | , vm{ v } | 80 | , initial_register{ r } |
| 81 | , vm{ d.controller->get_vm_instance() } | ||
| 83 | { | 82 | { |
| 84 | current_register = &initial_register; | 83 | current_register = &initial_register; |
| 85 | } | 84 | } |
| 86 | 85 | ||
| 87 | bool exit_break; | 86 | bool exit_break; |
| 88 | 87 | ||
| 88 | debugger &dbg; | ||
| 89 | const vm_t &vm; | 89 | const vm_t &vm; |
| 90 | const vm_registers_t *current_register; | 90 | const vm_registers_t *current_register; |
| 91 | const vm_registers_t &initial_register; | 91 | const vm_registers_t &initial_register; |
| @@ -125,10 +125,8 @@ namespace · 33 unmodified lines | |||
| 125 | auto register_string = std::stringstream{}; | 125 | auto register_string = std::stringstream{}; |
| 126 | 126 | ||
| 127 | register_string | 127 | register_string |
| 128 | << "Registers:\n" | ||
| 129 | << " Thread ID: " << r.thread.get_thread_id() << "\n" | 128 | << " Thread ID: " << r.thread.get_thread_id() << "\n" |
| 130 | << " Module: " << r.module_ref->module->module_name->str() << "\n" | 129 | << " Module: " << r.module_ref->module->module_name->str() << " PC: " << r.pc << "\n" |
| 131 | << " PC: " << r.pc << "\n" | ||
| 132 | << " Opcode: " << r.module_ref->code_section[r.pc].op << "\n"; | 130 | << " Opcode: " << r.module_ref->code_section[r.pc].op << "\n"; |
| 133 | 131 | ||
| 134 | return register_string.str(); | 132 | return register_string.str(); |
| @@ -231,8 +229,12 @@ namespace · 96 unmodified lines | |||
| 231 | } | 229 | } |
| 232 | else | 230 | else |
| 233 | { | 231 | { |
| 234 | ss << "alloc\n" | 232 | ss << "alloc - size: " << alloc->alloc_type->size_in_bytes << "\n"; |
| 235 | << "\tsize: " << alloc->alloc_type->size_in_bytes << "\n"; | 233 | |
| 234 | enum_pointer_fields(*alloc->alloc_type, alloc->get_allocation(), [&ss](pointer_t p, std::size_t o) | ||
| 235 | { | ||
| 236 | ss << " [" << o << "] " << vm_alloc_t::from_allocation(p); | ||
| 237 | }); | ||
| 236 | } | 238 | } |
| 237 | 239 | ||
| 238 | return ss; | 240 | return ss; |
| @@ -282,21 +284,6 @@ namespace · 43 unmodified lines | |||
| 282 | return ss; | 284 | return ss; |
| 283 | } | 285 | } |
| 284 | 286 | ||
| 285 | std::string to_string_frame_pointers(vm_registers_t &r) | ||
| 286 | { | ||
| 287 | auto frame = r.stack.peek_frame(); | ||
| 288 | if (frame == nullptr) | ||
| 289 | return "<empty>"; | ||
| 290 | |||
| 291 | auto frame_string = std::stringstream{}; | ||
| 292 | enum_pointer_fields(*frame->frame_type, frame->base(), [&frame_string](pointer_t p) | ||
| 293 | { | ||
| 294 | frame_string << std::showbase << std::hex << reinterpret_cast<uintptr_t>(p) << " : " << std::dec << vm_alloc_t::from_allocation(p); | ||
| 295 | }); | ||
| 296 | |||
| 297 | return frame_string.str(); | ||
| 298 | } | ||
| 299 | |||
| 300 | void cmd_continue(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) | 287 | void cmd_continue(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) |
| 301 | { | 288 | { |
| 302 | cxt.exit_break = true; | 289 | cxt.exit_break = true; |
| @@ -311,7 +298,6 @@ namespace · 8 unmodified lines | |||
| 311 | void cmd_print_threads(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) | 298 | void cmd_print_threads(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) |
| 312 | { | 299 | { |
| 313 | auto msg = std::stringstream{}; | 300 | auto msg = std::stringstream{}; |
| 314 | msg << "Threads:\n"; | ||
| 315 | for (auto t : cxt.vm.get_scheduler_control().get_all_threads()) | 301 | for (auto t : cxt.vm.get_scheduler_control().get_all_threads()) |
| 316 | msg << " " << std::setw(5) << t->get_thread_id() << " - " << t->get_state() << "\n"; | 302 | msg << " " << std::setw(5) << t->get_thread_id() << " - " << t->get_state() << "\n"; |
| 317 | 303 | ||
| @@ -321,21 +307,146 @@ namespace · 3 unmodified lines | |||
| 321 | void cmd_print_modules(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) | 307 | void cmd_print_modules(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) |
| 322 | { | 308 | { |
| 323 | auto msg = std::stringstream{}; | 309 | auto msg = std::stringstream{}; |
| 324 | msg << "Modules:\n"; | ||
| 325 | |||
| 326 | cxt.vm.enum_loaded_modules([&msg](const loaded_vm_module_t &m) | 310 | cxt.vm.enum_loaded_modules([&msg](const loaded_vm_module_t &m) |
| 327 | { | 311 | { |
| 328 | auto module_instance = m.module.lock(); | 312 | auto module_instance = m.module.lock(); |
| 329 | if (module_instance == nullptr) | 313 | if (module_instance != nullptr) |
| 330 | return; | 314 | { |
| 315 | msg << std::setw(3) << m.load_id << ": " | ||
| 316 | << std::setw(16) << module_instance->module_name->str() | ||
| 317 | << std::setw(0) << " - " << m.origin->str() << "\n"; | ||
| 318 | } | ||
| 331 | 319 | ||
| 332 | msg << std::setw(24) << module_instance->module_name->str() << std::setw(0) << " " << m.origin->str() << "\n"; | 320 | return true; |
| 333 | }); | 321 | }); |
| 334 | 322 | ||
| 335 | debug_print_info(msg.str()); | 323 | debug_print_info(msg.str()); |
| 336 | } | 324 | } |
| 337 | 325 | ||
| 338 | void cmd_stacktrace(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) | 326 | void cmd_breakpoint_set(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) |
| 327 | { | ||
| 328 | if (a.size() < 3) | ||
| 329 | { | ||
| 330 | debug_print_error("Must supply an IP and module ID"); | ||
| 331 | return; | ||
| 332 | } | ||
| 333 | |||
| 334 | vm_pc_t pc; | ||
| 335 | try | ||
| 336 | { | ||
| 337 | pc = std::stoul(a[1]); | ||
| 338 | } | ||
| 339 | catch (...) | ||
| 340 | { | ||
| 341 | debug_print_error("Invalid IP format"); | ||
| 342 | return; | ||
| 343 | } | ||
| 344 | |||
| 345 | uint32_t module_id; | ||
| 346 | try | ||
| 347 | { | ||
| 348 | module_id = std::stoul(a[2]); | ||
| 349 | } | ||
| 350 | catch (...) | ||
| 351 | { | ||
| 352 | debug_print_error("Invalid module ID format"); | ||
| 353 | return; | ||
| 354 | } | ||
| 355 | |||
| 356 | auto module = std::shared_ptr<vm_module_t>{}; | ||
| 357 | cxt.vm.enum_loaded_modules([module_id, &module](const loaded_vm_module_t &m) | ||
| 358 | { | ||
| 359 | if (module_id != m.load_id) | ||
| 360 | return true; | ||
| 361 | |||
| 362 | // Found the matching ID so stop | ||
| 363 | module = m.module.lock(); | ||
| 364 | return false; | ||
| 365 | }); | ||
| 366 | |||
| 367 | if (module == nullptr) | ||
| 368 | { | ||
| 369 | debug_print_error("Invalid module ID"); | ||
| 370 | return; | ||
| 371 | } | ||
| 372 | else if (util::has_flag(module->header.runtime_flag, runtime_flags_t::builtin)) | ||
| 373 | { | ||
| 374 | debug_print_error("Unable to set breakpoint in built-in module"); | ||
| 375 | return; | ||
| 376 | } | ||
| 377 | |||
| 378 | if (pc < 0 || module->code_section.size() <= static_cast<std::size_t>(pc)) | ||
| 379 | { | ||
| 380 | debug_print_error("Invalid IP for module"); | ||
| 381 | return; | ||
| 382 | } | ||
| 383 | |||
| 384 | cxt.dbg.breakpoint_cookies.push_back(cxt.dbg.controller->set_breakpoint(module, pc)); | ||
| 385 | } | ||
| 386 | |||
| 387 | void cmd_breakpoint_clear(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) | ||
| 388 | { | ||
| 389 | if (a.size() == 1) | ||
| 390 | { | ||
| 391 | debug_print_error("Must supply a breakpoint ID or wildcard"); | ||
| 392 | return; | ||
| 393 | } | ||
| 394 | |||
| 395 | auto controller = cxt.dbg.controller; | ||
| 396 | if (a[1].compare("*") == 0) | ||
| 397 | { | ||
| 398 | // Clear all breakpoints | ||
| 399 | for (auto cookie : cxt.dbg.breakpoint_cookies) | ||
| 400 | controller->clear_breakpoint(cookie); | ||
| 401 | |||
| 402 | cxt.dbg.breakpoint_cookies.clear(); | ||
| 403 | return; | ||
| 404 | } | ||
| 405 | |||
| 406 | // Clear specific breakpoint | ||
| 407 | cookie_t breakpoint_cookie; | ||
| 408 | try | ||
| 409 | { | ||
| 410 | breakpoint_cookie = static_cast<cookie_t>(std::stoul(a[1])); | ||
| 411 | } | ||
| 412 | catch (...) | ||
| 413 | { | ||
| 414 | debug_print_error("Invalid breakpoint ID format"); | ||
| 415 | return; | ||
| 416 | } | ||
| 417 | |||
| 418 | auto cookie_iter = std::find_if( | ||
| 419 | std::begin(cxt.dbg.breakpoint_cookies), | ||
| 420 | std::end(cxt.dbg.breakpoint_cookies), | ||
| 421 | [breakpoint_cookie] (const cookie_t c) { return c == breakpoint_cookie; }); | ||
| 422 | if (cookie_iter == cxt.dbg.breakpoint_cookies.end()) | ||
| 423 | { | ||
| 424 | debug_print_error("Unknown breakpoint ID"); | ||
| 425 | return; | ||
| 426 | } | ||
| 427 | |||
| 428 | // Clear the breakpoint | ||
| 429 | controller->clear_breakpoint(breakpoint_cookie); | ||
| 430 | |||
| 431 | // Remove the cookie from the list | ||
| 432 | cxt.dbg.breakpoint_cookies.erase(cookie_iter); | ||
| 433 | } | ||
| 434 | |||
| 435 | void cmd_breakpoint_list(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) | ||
| 436 | { | ||
| 437 | auto msg = std::stringstream{}; | ||
| 438 | for (auto cookie : cxt.dbg.breakpoint_cookies) | ||
| 439 | { | ||
| 440 | const auto details = cxt.dbg.controller->get_breakpoint_details(cookie); | ||
| 441 | msg << std::setw(3) << cookie << ": " | ||
| 442 | << std::setw(16) << details.module->module_name->str() | ||
| 443 | << std::setw(0) << " @ " << details.pc << "\n"; | ||
| 444 | } | ||
| 445 | |||
| 446 | debug_print_info(msg.str()); | ||
| 447 | } | ||
| 448 | |||
| 449 | void cmd_stackwalk(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) | ||
| 339 | { | 450 | { |
| 340 | auto thread_registers = std::map<uint32_t, const vm_registers_t*>{}; | 451 | auto thread_registers = std::map<uint32_t, const vm_registers_t*>{}; |
| 341 | if (a.size() == 1) | 452 | if (a.size() == 1) |
| @@ -345,7 +456,6 @@ namespace · 3 unmodified lines | |||
| 345 | else | 456 | else |
| 346 | { | 457 | { |
| 347 | uint32_t thread_id; | 458 | uint32_t thread_id; |
| 348 | auto all_threads = cxt.vm.get_scheduler_control().get_all_threads(); | ||
| 349 | if (a[1].compare("*") == 0) | 459 | if (a[1].compare("*") == 0) |
| 350 | { | 460 | { |
| 351 | thread_id = 0; | 461 | thread_id = 0; |
| @@ -354,7 +464,7 @@ namespace · 2 unmodified lines | |||
| 354 | { | 464 | { |
| 355 | try | 465 | try |
| 356 | { | 466 | { |
| 357 | thread_id = std::stoi(a[1]); | 467 | thread_id = std::stoul(a[1]); |
| 358 | } | 468 | } |
| 359 | catch (...) | 469 | catch (...) |
| 360 | { | 470 | { |
| @@ -364,6 +474,7 @@ namespace · 3 unmodified lines | |||
| 364 | } | 474 | } |
| 365 | 475 | ||
| 366 | // Find the thread(s) | 476 | // Find the thread(s) |
| 477 | auto all_threads = cxt.vm.get_scheduler_control().get_all_threads(); | ||
| 367 | for (auto t : all_threads) | 478 | for (auto t : all_threads) |
| 368 | { | 479 | { |
| 369 | const auto tid = t->get_thread_id(); | 480 | const auto tid = t->get_thread_id(); |
| @@ -406,7 +517,7 @@ namespace · 36 unmodified lines | |||
| 406 | uint32_t thread_id; | 517 | uint32_t thread_id; |
| 407 | try | 518 | try |
| 408 | { | 519 | { |
| 409 | thread_id = std::stoi(a[1]); | 520 | thread_id = std::stoul(a[1]); |
| 410 | } | 521 | } |
| 411 | catch (...) | 522 | catch (...) |
| 412 | { | 523 | { |
| @@ -432,7 +543,7 @@ namespace · 19 unmodified lines | |||
| 432 | 543 | ||
| 433 | void cmd_disassemble(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) | 544 | void cmd_disassemble(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) |
| 434 | { | 545 | { |
| 435 | auto disassemble_length = 1; | 546 | auto disassemble_length = int{ 1 }; |
| 436 | if (a.size() > 1) | 547 | if (a.size() > 1) |
| 437 | { | 548 | { |
| 438 | try | 549 | try |
| @@ -629,11 +740,17 @@ namespace · 190 unmodified lines | |||
| 629 | const dbg_cmd_t cmds[] = | 740 | const dbg_cmd_t cmds[] = |
| 630 | { | 741 | { |
| 631 | { "c", "Continue", nullptr, nullptr, cmd_continue }, | 742 | { "c", "Continue", nullptr, nullptr, cmd_continue }, |
| 743 | |||
| 632 | { "r", "Print registers", nullptr, nullptr, cmd_print_registers }, | 744 | { "r", "Print registers", nullptr, nullptr, cmd_print_registers }, |
| 633 | { "t", "Print all threads", nullptr, nullptr, cmd_print_threads }, | 745 | { "t", "Print all threads", nullptr, nullptr, cmd_print_threads }, |
| 634 | { "m", "Print all loaded modules", nullptr, nullptr, cmd_print_modules }, | 746 | { "m", "Print all loaded modules", nullptr, nullptr, cmd_print_modules }, |
| 635 | { "bt", "Stack walk/back trace for the current or supplied thread", "bt ([0-9]+|\\*)?", "bt, bt 34, bt *", cmd_stacktrace }, | 747 | |
| 636 | { "st", "Switch to thread", "st [0-9]+", "st 42", cmd_switchthread }, | 748 | { "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)", cmd_breakpoint_set }, |
| 749 | { "bc", "Clear breakpoint(s)", "bc ([0-9]+|\\*)", "bc 3, bc *", cmd_breakpoint_clear }, | ||
| 750 | { "bl", "List breakpoints", nullptr, nullptr, cmd_breakpoint_list }, | ||
| 751 | |||
| 752 | { "sw", "Stack walk for the current or supplied thread", "sw ([0-9]+|\\*)?", "sw, sw 34, sw *", cmd_stackwalk }, | ||
| 753 | { "~", "Switch to thread", "~ [0-9]+", "~ 42", cmd_switchthread }, | ||
| 637 | { "d", "Disassemble the next/previous N instructions", "d (-?[0-9]+)?", "d, d -4, d 5", cmd_disassemble }, | 754 | { "d", "Disassemble the next/previous N instructions", "d (-?[0-9]+)?", "d, d -4, d 5", cmd_disassemble }, |
| 638 | { "x", "Examine memory", "x [mp|fp] [0-9]+ ([0-9]+)?", "x mp 3, x fp 20 6", cmd_examine }, | 755 | { "x", "Examine memory", "x [mp|fp] [0-9]+ ([0-9]+)?", "x mp 3, x fp 20 6", cmd_examine }, |
| 639 | { "?", "Print help", nullptr, nullptr, cmd_help }, | 756 | { "?", "Print help", nullptr, nullptr, cmd_help }, |
| @@ -703,7 +820,7 @@ namespace · 63 unmodified lines | |||
| 703 | return split_string; | 820 | return split_string; |
| 704 | } | 821 | } |
| 705 | 822 | ||
| 706 | void prompt(const vm_t &vm, const vm_registers_t &r) | 823 | void prompt(debugger &debugger, const vm_registers_t &r) |
| 707 | { | 824 | { |
| 708 | std::cout | 825 | std::cout |
| 709 | << console_modifiers::green_font | 826 | << console_modifiers::green_font |
| @@ -711,10 +828,9 @@ namespace · 1 unmodified line | |||
| 711 | << "'?' for help\n" | 828 | << "'?' for help\n" |
| 712 | << console_modifiers::reset_all; | 829 | << console_modifiers::reset_all; |
| 713 | 830 | ||
| 714 | std::locale loc; | ||
| 715 | std::string cmd; | 831 | std::string cmd; |
| 716 | 832 | ||
| 717 | dbg_cmd_cxt_t cxt{ vm, r }; | 833 | dbg_cmd_cxt_t cxt{ debugger, r }; |
| 718 | for (;;) | 834 | for (;;) |
| 719 | { | 835 | { |
| 720 | std::cout | 836 | std::cout |
| @@ -746,12 +862,17 @@ namespace · 25 unmodified lines | |||
| 746 | } | 862 | } |
| 747 | } | 863 | } |
| 748 | 864 | ||
| 749 | void debugger::on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t tool_id) | 865 | debugger::debugger() |
| 866 | : _tool_id{ 0 } | ||
| 867 | , controller{ nullptr } | ||
| 868 | { } | ||
| 869 | |||
| 870 | void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id) | ||
| 750 | { | 871 | { |
| 751 | _tool_id = tool_id; | 872 | _tool_id = tool_id; |
| 752 | _vm = &vm; | 873 | controller = &controller_; |
| 753 | 874 | ||
| 754 | _event_cookies.push_back(controller.subscribe_event(vm_event_t::thread_begin, [](vm_event_t, vm_event_context_t &cxt) | 875 | _event_cookies.push_back(controller->subscribe_event(vm_event_t::thread_begin, [](vm_event_t, vm_event_context_t &cxt) |
| 755 | { | 876 | { |
| 756 | auto t = cxt.value1.thread; | 877 | auto t = cxt.value1.thread; |
| 757 | auto ss = std::stringstream{}; | 878 | auto ss = std::stringstream{}; |
| @@ -765,7 +886,7 @@ void debugger::on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t t · 7 unmodified lines | |||
| 765 | debug_print_info(ss.str()); | 886 | debug_print_info(ss.str()); |
| 766 | })); | 887 | })); |
| 767 | 888 | ||
| 768 | _event_cookies.push_back(controller.subscribe_event(vm_event_t::thread_end, [](vm_event_t, vm_event_context_t &cxt) | 889 | _event_cookies.push_back(controller->subscribe_event(vm_event_t::thread_end, [](vm_event_t, vm_event_context_t &cxt) |
| 769 | { | 890 | { |
| 770 | auto ss = std::stringstream{}; | 891 | auto ss = std::stringstream{}; |
| 771 | ss << "Thread " | 892 | ss << "Thread " |
| @@ -775,7 +896,7 @@ void debugger::on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t t · 3 unmodified lines | |||
| 775 | debug_print_info(ss.str()); | 896 | debug_print_info(ss.str()); |
| 776 | })); | 897 | })); |
| 777 | 898 | ||
| 778 | _event_cookies.push_back(controller.subscribe_event(vm_event_t::exception_raised, [](vm_event_t, vm_event_context_t &cxt) | 899 | _event_cookies.push_back(controller->subscribe_event(vm_event_t::exception_raised, [](vm_event_t, vm_event_context_t &cxt) |
| 779 | { | 900 | { |
| 780 | auto ss = std::stringstream{}; | 901 | auto ss = std::stringstream{}; |
| 781 | ss << "Exception '" | 902 | ss << "Exception '" |
| @@ -785,7 +906,7 @@ void debugger::on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t t · 3 unmodified lines | |||
| 785 | debug_print_info(ss.str()); | 906 | debug_print_info(ss.str()); |
| 786 | })); | 907 | })); |
| 787 | 908 | ||
| 788 | _event_cookies.push_back(controller.subscribe_event(vm_event_t::exception_unhandled, [&](vm_event_t, vm_event_context_t &cxt) | 909 | _event_cookies.push_back(controller->subscribe_event(vm_event_t::exception_unhandled, [&](vm_event_t, vm_event_context_t &cxt) |
| 789 | { | 910 | { |
| 790 | auto ss = std::stringstream{}; | 911 | auto ss = std::stringstream{}; |
| 791 | ss << "Unhandled exception '" | 912 | ss << "Unhandled exception '" |
| @@ -793,37 +914,46 @@ void debugger::on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t t · 1 unmodified line | |||
| 793 | << "'"; | 914 | << "'"; |
| 794 | 915 | ||
| 795 | debug_print_error(ss.str()); | 916 | debug_print_error(ss.str()); |
| 796 | prompt(*_vm, *cxt.value1.registers); | 917 | prompt(*this, *cxt.value1.registers); |
| 797 | })); | 918 | })); |
| 798 | 919 | ||
| 799 | _event_cookies.push_back(controller.subscribe_event(vm_event_t::module_load, [](vm_event_t, vm_event_context_t &cxt) | 920 | _event_cookies.push_back(controller->subscribe_event(vm_event_t::module_thread_load, [&](vm_event_t, vm_event_context_t &cxt) |
| 800 | { | 921 | { |
| 801 | auto m = (*cxt.value1.module); | 922 | auto m = (*cxt.value2.module); |
| 802 | auto ss = std::stringstream{}; | 923 | auto ss = std::stringstream{}; |
| 803 | ss << "Module " | 924 | ss << "Module " |
| 804 | << m->module_name->str() | 925 | << m->module_name->str() |
| 805 | << " loaded ("; | 926 | << " loaded"; |
| 806 | 927 | ||
| 807 | if (!util::has_flag(m->header.runtime_flag, runtime_flags_t::builtin)) | 928 | if (util::has_flag(m->header.runtime_flag, runtime_flags_t::builtin)) |
| 808 | ss << cxt.value2.str->str(); | 929 | ss << " (built-in)"; |
| 809 | else | ||
| 810 | ss << "built-in"; | ||
| 811 | 930 | ||
| 812 | ss << ")"; | ||
| 813 | 931 | ||
| 814 | debug_print_info(ss.str()); | 932 | debug_print_info(ss.str()); |
| 815 | })); | 933 | })); |
| 816 | 934 | ||
| 817 | _controller = &controller; | 935 | _event_cookies.push_back(controller->subscribe_event(vm_event_t::breakpoint, [&](vm_event_t, vm_event_context_t &cxt) |
| 936 | { | ||
| 937 | auto ss = std::stringstream{}; | ||
| 938 | ss << "Breakpoint hit"; | ||
| 939 | if (cxt.value2.cookie != 0) | ||
| 940 | ss << " [" << cxt.value2.cookie << "]"; | ||
| 941 | |||
| 942 | debug_print_info(ss.str()); | ||
| 943 | |||
| 944 | auto reg_str = registers_to_string(*cxt.value1.registers); | ||
| 945 | debug_print_info(reg_str); | ||
| 946 | |||
| 947 | prompt(*this, *cxt.value1.registers); | ||
| 948 | })); | ||
| 818 | } | 949 | } |
| 819 | 950 | ||
| 820 | void debugger::on_unload() | 951 | void debugger::on_unload() |
| 821 | { | 952 | { |
| 822 | assert(_controller != nullptr); | 953 | assert(controller != nullptr); |
| 823 | 954 | ||
| 824 | for (auto ec : _event_cookies) | 955 | for (auto ec : _event_cookies) |
| 825 | _controller->unsubscribe_event(ec); | 956 | controller->unsubscribe_event(ec); |
| 826 | 957 | ||
| 827 | _controller = nullptr; | 958 | controller = nullptr; |
| 828 | _vm = nullptr; | ||
| 829 | } | 959 | } |
| @@ -10,20 +10,25 @@ · 9 unmodified lines | |||
| 10 | #include <cstdint> | 10 | #include <cstdint> |
| 11 | #include <vector> | 11 | #include <vector> |
| 12 | #include <disvm.h> | 12 | #include <disvm.h> |
| 13 | #include <vm_tools.h> | ||
| 13 | 14 | ||
| 14 | class debugger final : public disvm::runtime::vm_tool_t | 15 | class debugger final : public disvm::runtime::vm_tool_t |
| 15 | { | 16 | { |
| 17 | public: | ||
| 18 | debugger(); | ||
| 19 | |||
| 16 | public: // vm_tool_t | 20 | public: // vm_tool_t |
| 17 | void on_load(disvm::vm_t &vm, disvm::runtime::vm_tool_controller_t &controller, std::size_t tool_id); | 21 | void on_load(disvm::runtime::vm_tool_controller_t &controller, std::size_t tool_id) override; |
| 22 | |||
| 23 | void on_unload() override; | ||
| 18 | 24 | ||
| 19 | void on_unload(); | 25 | public: |
| 26 | disvm::runtime::vm_tool_controller_t *controller; | ||
| 27 | std::vector<disvm::runtime::cookie_t> breakpoint_cookies; | ||
| 20 | 28 | ||
| 21 | private: | 29 | private: |
| 22 | disvm::vm_t *_vm; | ||
| 23 | std::size_t _tool_id; | 30 | std::size_t _tool_id; |
| 24 | disvm::runtime::vm_tool_controller_t *_controller; | 31 | std::vector<disvm::runtime::cookie_t> _event_cookies; |
| 25 | std::vector<std::size_t> _event_cookies; | ||
| 26 | std::vector<std::size_t> _breakpoint_cookies; | ||
| 27 | }; | 32 | }; |
| 28 | 33 | ||
| 29 | #endif // _DISVM_SRC_EXEC_EXEC_H_ | 34 | #endif // _DISVM_SRC_EXEC_EXEC_H_ |
| @@ -39,6 +39,8 @@ namespace disvm · 38 unmodified lines | |||
| 39 | class loaded_vm_module_t final | 39 | class loaded_vm_module_t final |
| 40 | { | 40 | { |
| 41 | public: | 41 | public: |
| 42 | std::size_t load_id; | ||
| 43 | |||
| 42 | // Origin of module | 44 | // Origin of module |
| 43 | std::unique_ptr<runtime::vm_string_t> origin; | 45 | std::unique_ptr<runtime::vm_string_t> origin; |
| 44 | 46 | ||
| @@ -46,7 +48,8 @@ namespace disvm · 1 unmodified line | |||
| 46 | std::weak_ptr<runtime::vm_module_t> module; | 48 | std::weak_ptr<runtime::vm_module_t> module; |
| 47 | }; | 49 | }; |
| 48 | 50 | ||
| 49 | using loaded_vm_module_callback_t = std::function<void(const loaded_vm_module_t&)>; | 51 | // Callback for enumerating loaded modules - return 'false' to stop enumeration, otherwise 'true' |
| 52 | using loaded_vm_module_callback_t = std::function<bool(const loaded_vm_module_t&)>; | ||
| 50 | 53 | ||
| 51 | // Virtual machine | 54 | // Virtual machine |
| 52 | class vm_t final | 55 | class vm_t final |
| @@ -824,13 +824,12 @@ namespace disvm · 823 unmodified lines | |||
| 824 | virtual void set_tool_dispatch_on_all_threads(vm_tool_dispatch_t *dispatch) = 0; | 824 | virtual void set_tool_dispatch_on_all_threads(vm_tool_dispatch_t *dispatch) = 0; |
| 825 | }; | 825 | }; |
| 826 | 826 | ||
| 827 | // The supplied allocation is guaranteed to be valid for the lifetime of the callback. | ||
| 828 | using vm_alloc_callback_t = std::function<void(const vm_alloc_t *)>; | ||
| 829 | |||
| 827 | // VM garbage collector interface | 830 | // VM garbage collector interface |
| 828 | class vm_garbage_collector_t | 831 | class vm_garbage_collector_t |
| 829 | { | 832 | { |
| 830 | public: | ||
| 831 | // The supplied allocation is guaranteed to be valid for the lifetime of the callback. | ||
| 832 | using vm_alloc_callback_t = std::function<void(const vm_alloc_t *)>; | ||
| 833 | |||
| 834 | public: | 833 | public: |
| 835 | virtual ~vm_garbage_collector_t() = 0; | 834 | virtual ~vm_garbage_collector_t() = 0; |
| 836 | 835 | ||
| @@ -862,7 +861,7 @@ namespace disvm · 25 unmodified lines | |||
| 862 | virtual ~vm_tool_t() = 0; | 861 | virtual ~vm_tool_t() = 0; |
| 863 | 862 | ||
| 864 | // Called when the tool is loaded into the VM | 863 | // Called when the tool is loaded into the VM |
| 865 | virtual void on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t tool_id) = 0; | 864 | virtual void on_load(vm_tool_controller_t &controller, std::size_t tool_id) = 0; |
| 866 | 865 | ||
| 867 | // Called when the tool is unloaded from the VM. | 866 | // Called when the tool is unloaded from the VM. |
| 868 | // Note once the tool has been unloaded the vm and controller references supplied during load | 867 | // Note once the tool has been unloaded the vm and controller references supplied during load |
| @@ -70,10 +70,11 @@ namespace disvm · 69 unmodified lines | |||
| 70 | // Increment the ref count of all points in the supplied memory allocation | 70 | // Increment the ref count of all points in the supplied memory allocation |
| 71 | void inc_ref_count_in_memory(const type_descriptor_t &type_desc, void *data); | 71 | void inc_ref_count_in_memory(const type_descriptor_t &type_desc, void *data); |
| 72 | 72 | ||
| 73 | // Decrement the ref count and if 0 free the allocation. | 73 | // Decrement the ref count and if 0 free the allocation |
| 74 | void dec_ref_count_and_free(vm_alloc_t *alloc); | 74 | void dec_ref_count_and_free(vm_alloc_t *alloc); |
| 75 | 75 | ||
| 76 | using pointer_field_callback_t = std::function<void(pointer_t)>; | 76 | // Callback for a pointer field supplying the pointer and byte offset |
| 77 | using pointer_field_callback_t = std::function<void(pointer_t, std::size_t)>; | ||
| 77 | 78 | ||
| 78 | // The callback will be called on all non-null pointer fields. | 79 | // The callback will be called on all non-null pointer fields. |
| 79 | inline void enum_pointer_fields(const type_descriptor_t &type_desc, void *data, pointer_field_callback_t callback) | 80 | inline void enum_pointer_fields(const type_descriptor_t &type_desc, void *data, pointer_field_callback_t callback) |
| @@ -82,8 +83,9 @@ namespace disvm · 2 unmodified lines | |||
| 82 | if (type_desc.size_in_bytes == 0) | 83 | if (type_desc.size_in_bytes == 0) |
| 83 | return; | 84 | return; |
| 84 | 85 | ||
| 86 | auto offset_accum = std::size_t{ 0 }; | ||
| 85 | auto memory = reinterpret_cast<word_t *>(data); | 87 | auto memory = reinterpret_cast<word_t *>(data); |
| 86 | for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8) | 88 | for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8, offset_accum += 8) |
| 87 | { | 89 | { |
| 88 | const auto words8 = type_desc.pointer_map[i]; | 90 | const auto words8 = type_desc.pointer_map[i]; |
| 89 | if (words8 != 0) | 91 | if (words8 != 0) |
| @@ -91,14 +93,14 @@ namespace disvm · 1 unmodified line | |||
| 91 | const auto flags = std::bitset<sizeof(words8) * 8>{ words8 }; | 93 | const auto flags = std::bitset<sizeof(words8) * 8>{ words8 }; |
| 92 | 94 | ||
| 93 | // Enumerating the flags in reverse order so memory access is sequential. | 95 | // Enumerating the flags in reverse order so memory access is sequential. |
| 94 | if (flags[7] && (memory[0] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[0])); | 96 | if (flags[7] && (memory[0] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[0]), offset_accum); |
| 95 | if (flags[6] && (memory[1] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[1])); | 97 | if (flags[6] && (memory[1] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[1]), offset_accum + (1 * sizeof(pointer_t))); |
| 96 | if (flags[5] && (memory[2] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[2])); | 98 | if (flags[5] && (memory[2] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[2]), offset_accum + (2 * sizeof(pointer_t))); |
| 97 | if (flags[4] && (memory[3] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[3])); | 99 | if (flags[4] && (memory[3] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[3]), offset_accum + (3 * sizeof(pointer_t))); |
| 98 | if (flags[3] && (memory[4] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[4])); | 100 | if (flags[3] && (memory[4] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[4]), offset_accum + (4 * sizeof(pointer_t))); |
| 99 | if (flags[2] && (memory[5] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[5])); | 101 | if (flags[2] && (memory[5] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[5]), offset_accum + (5 * sizeof(pointer_t))); |
| 100 | if (flags[1] && (memory[6] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[6])); | 102 | if (flags[1] && (memory[6] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[6]), offset_accum + (6 * sizeof(pointer_t))); |
| 101 | if (flags[0] && (memory[7] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[7])); | 103 | if (flags[0] && (memory[7] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[7]), offset_accum + (7 * sizeof(pointer_t))); |
| 102 | } | 104 | } |
| 103 | } | 105 | } |
| 104 | } | 106 | } |
| @@ -17,10 +17,12 @@ namespace disvm · 16 unmodified lines | |||
| 17 | { | 17 | { |
| 18 | namespace runtime | 18 | namespace runtime |
| 19 | { | 19 | { |
| 20 | using cookie_t = std::size_t; | ||
| 21 | |||
| 20 | // Value in event context | 22 | // Value in event context |
| 21 | union vm_context_value_t | 23 | union vm_context_value_t |
| 22 | { | 24 | { |
| 23 | std::size_t i; | 25 | cookie_t cookie; |
| 24 | std::shared_ptr<vm_module_t> *module; // Note this is a pointer to std::shared_ptr<> | 26 | std::shared_ptr<vm_module_t> *module; // Note this is a pointer to std::shared_ptr<> |
| 25 | vm_registers_t *registers; | 27 | vm_registers_t *registers; |
| 26 | const vm_thread_t *thread; | 28 | const vm_thread_t *thread; |
| @@ -39,8 +41,11 @@ namespace disvm · 12 unmodified lines | |||
| 39 | // List of possible events | 41 | // List of possible events |
| 40 | enum class vm_event_t | 42 | enum class vm_event_t |
| 41 | { | 43 | { |
| 42 | module_load, // value1: std::shared_ptr<vm_module_t> | 44 | module_vm_load, // value1: std::shared_ptr<vm_module_t> |
| 43 | // value2: const vm_string_t - path | 45 | // value2: const vm_string_t - path on disk |
| 46 | |||
| 47 | module_thread_load, // value1: vm_registers_t | ||
| 48 | // value2: std::shared_ptr<vm_module_t> | ||
| 44 | 49 | ||
| 45 | thread_begin, // value1: const vm_thread_t | 50 | thread_begin, // value1: const vm_thread_t |
| 46 | thread_end, // value1: const vm_thread_t | 51 | thread_end, // value1: const vm_thread_t |
| @@ -54,22 +59,32 @@ namespace disvm · 7 unmodified lines | |||
| 54 | // value3: const vm_alloc_t - object raised | 59 | // value3: const vm_alloc_t - object raised |
| 55 | 60 | ||
| 56 | breakpoint, // value1: vm_registers_t | 61 | breakpoint, // value1: vm_registers_t |
| 57 | // value2: std::size_t - cookie of breakpoint. '0' indicates the breakpoint was inserted by the VM or compiler. | 62 | // value2: cookie_t - cookie of breakpoint. '0' indicates the breakpoint was inserted by the VM or compiler. |
| 58 | }; | 63 | }; |
| 59 | 64 | ||
| 60 | using vm_event_callback_t = std::function<void(vm_event_t, vm_event_context_t &)>; | 65 | using vm_event_callback_t = std::function<void(vm_event_t, vm_event_context_t &)>; |
| 61 | 66 | ||
| 67 | struct breakpoint_details_t final | ||
| 68 | { | ||
| 69 | std::shared_ptr<vm_module_t> module; | ||
| 70 | vm_pc_t pc; | ||
| 71 | }; | ||
| 72 | |||
| 62 | // VM tool controller | 73 | // VM tool controller |
| 63 | class vm_tool_controller_t | 74 | class vm_tool_controller_t |
| 64 | { | 75 | { |
| 65 | public: | 76 | public: |
| 77 | // The current vm instance | ||
| 78 | virtual vm_t& get_vm_instance() const = 0; | ||
| 79 | |||
| 66 | // Call the supplied callback when the specified event occurs | 80 | // Call the supplied callback when the specified event occurs |
| 67 | virtual std::size_t subscribe_event(vm_event_t, vm_event_callback_t) = 0; | 81 | virtual cookie_t subscribe_event(vm_event_t, vm_event_callback_t) = 0; |
| 68 | virtual void unsubscribe_event(std::size_t) = 0; | 82 | virtual void unsubscribe_event(cookie_t) = 0; |
| 69 | 83 | ||
| 70 | // Set a breakpoint in the supplied module at the specified PC location | 84 | // Set a breakpoint in the supplied module at the specified PC location |
| 71 | virtual std::size_t set_breakpoint(std::shared_ptr<vm_module_t>, vm_pc_t) = 0; | 85 | virtual cookie_t set_breakpoint(std::shared_ptr<vm_module_t>, vm_pc_t) = 0; |
| 72 | virtual void clear_breakpoint(std::size_t) = 0; | 86 | virtual breakpoint_details_t get_breakpoint_details(cookie_t) const = 0; |
| 87 | virtual void clear_breakpoint(cookie_t) = 0; | ||
| 73 | }; | 88 | }; |
| 74 | } | 89 | } |
| 75 | } | 90 | } |
| @@ -1750,12 +1750,13 @@ namespace · 1749 unmodified lines | |||
| 1750 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); | 1750 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); |
| 1751 | pt_ref(r.dest) = nullptr; | 1751 | pt_ref(r.dest) = nullptr; |
| 1752 | 1752 | ||
| 1753 | auto imported_module = std::shared_ptr<const vm_module_t>{}; | 1753 | auto imported_module = std::shared_ptr<vm_module_t>{}; |
| 1754 | 1754 | ||
| 1755 | // Handle self loading module optimization. | 1755 | // Handle self loading module optimization. |
| 1756 | if (std::strcmp(str->str(), "$self") == 0) | 1756 | if (std::strcmp(str->str(), "$self") == 0) |
| 1757 | { | 1757 | { |
| 1758 | imported_module = r.module_ref->module; | 1758 | // Remove 'const' from the module type in the shared_ptr<> |
| 1759 | imported_module = std::const_pointer_cast<vm_module_t>(r.module_ref->module); | ||
| 1759 | } | 1760 | } |
| 1760 | else | 1761 | else |
| 1761 | { | 1762 | { |
| @@ -1779,6 +1780,11 @@ namespace · 17 unmodified lines | |||
| 1779 | 1780 | ||
| 1780 | auto entry_module_ref = new vm_module_ref_t{ imported_module, function_imports }; | 1781 | auto entry_module_ref = new vm_module_ref_t{ imported_module, function_imports }; |
| 1781 | pt_ref(r.dest) = entry_module_ref->get_allocation(); | 1782 | pt_ref(r.dest) = entry_module_ref->get_allocation(); |
| 1783 | |||
| 1784 | // Check if the tool dispatcher has been supplied | ||
| 1785 | auto tool_dispatch = r.tool_dispatch.load(); | ||
| 1786 | if (tool_dispatch != nullptr) | ||
| 1787 | tool_dispatch->on_module_thread_load(r, imported_module); | ||
| 1782 | } | 1788 | } |
| 1783 | } | 1789 | } |
| 1784 | 1790 | ||
| @@ -22,7 +22,7 @@ vm_garbage_collector_t::~vm_garbage_collector_t() · 21 unmodified lines | |||
| 22 | 22 | ||
| 23 | std::unique_ptr<vm_garbage_collector_t> disvm::runtime::create_no_op_gc(vm_t &) | 23 | std::unique_ptr<vm_garbage_collector_t> disvm::runtime::create_no_op_gc(vm_t &) |
| 24 | { | 24 | { |
| 25 | class no_op_gc : public vm_garbage_collector_t | 25 | class no_op_gc final : public vm_garbage_collector_t |
| 26 | { | 26 | { |
| 27 | public: | 27 | public: |
| 28 | void track_allocation(vm_alloc_t *) override | 28 | void track_allocation(vm_alloc_t *) override |
| @@ -118,7 +118,7 @@ namespace · 89 unmodified lines | |||
| 118 | { | 118 | { |
| 119 | std::queue<vm_alloc_t *> allocs; | 119 | std::queue<vm_alloc_t *> allocs; |
| 120 | 120 | ||
| 121 | auto add_pointer_to_queue = std::function<void(pointer_t p)>{ [&allocs](pointer_t p) -> void | 121 | auto add_pointer_to_queue = std::function<void(pointer_t p, std::size_t)>{ [&allocs](pointer_t p, std::size_t) -> void |
| 122 | { | 122 | { |
| 123 | auto a = vm_alloc_t::from_allocation(p); | 123 | auto a = vm_alloc_t::from_allocation(p); |
| 124 | allocs.push(a); | 124 | allocs.push(a); |
| @@ -155,7 +155,7 @@ namespace · 30 unmodified lines | |||
| 155 | debug::log_msg(debug::component_trace_t::garbage_collector, debug::log_level_t::debug, "gc: roots found: %" PRIuPTR "\n", allocs.size()); | 155 | debug::log_msg(debug::component_trace_t::garbage_collector, debug::log_level_t::debug, "gc: roots found: %" PRIuPTR "\n", allocs.size()); |
| 156 | 156 | ||
| 157 | // If logging is enabled update the callback with logging | 157 | // If logging is enabled update the callback with logging |
| 158 | add_pointer_to_queue = std::function<void(pointer_t p)>{ [&allocs](pointer_t p) -> void | 158 | add_pointer_to_queue = std::function<void(pointer_t p, std::size_t)>{ [&allocs](pointer_t p, std::size_t) -> void |
| 159 | { | 159 | { |
| 160 | auto a = vm_alloc_t::from_allocation(p); | 160 | auto a = vm_alloc_t::from_allocation(p); |
| 161 | debug::log_msg(debug::component_trace_t::garbage_collector, debug::log_level_t::debug, "\tref: %#" PRIxPTR "\n", a); | 161 | debug::log_msg(debug::component_trace_t::garbage_collector, debug::log_level_t::debug, "\tref: %#" PRIxPTR "\n", a); |
| @@ -26,11 +26,11 @@ namespace disvm · 25 unmodified lines | |||
| 26 | ~default_garbage_collector_t(); | 26 | ~default_garbage_collector_t(); |
| 27 | 27 | ||
| 28 | public: // vm_garbage_collector_t | 28 | public: // vm_garbage_collector_t |
| 29 | void track_allocation(vm_alloc_t *alloc) override final; | 29 | void track_allocation(vm_alloc_t *alloc) override; |
| 30 | 30 | ||
| 31 | void enum_tracked_allocations(vm_alloc_callback_t callback) const override final; | 31 | void enum_tracked_allocations(vm_alloc_callback_t callback) const override; |
| 32 | 32 | ||
| 33 | bool collect(std::vector<std::shared_ptr<const vm_thread_t>> &threads) override final; | 33 | bool collect(std::vector<std::shared_ptr<const vm_thread_t>> &threads) override; |
| 34 | 34 | ||
| 35 | private: | 35 | private: |
| 36 | vm_t &_vm; | 36 | vm_t &_vm; |
| @@ -34,18 +34,18 @@ namespace disvm · 33 unmodified lines | |||
| 34 | ~default_scheduler_t(); | 34 | ~default_scheduler_t(); |
| 35 | 35 | ||
| 36 | public: // vm_scheduler_t | 36 | public: // vm_scheduler_t |
| 37 | bool is_idle() const override final; | 37 | bool is_idle() const override; |
| 38 | 38 | ||
| 39 | vm_scheduler_control_t &get_controller() const override final; | 39 | vm_scheduler_control_t &get_controller() const override; |
| 40 | 40 | ||
| 41 | const vm_thread_t& schedule_thread(std::unique_ptr<vm_thread_t> thread) override final; | 41 | const vm_thread_t& schedule_thread(std::unique_ptr<vm_thread_t> thread) override; |
| 42 | 42 | ||
| 43 | void set_tool_dispatch_on_all_threads(vm_tool_dispatch_t *dispatch) override final; | 43 | void set_tool_dispatch_on_all_threads(vm_tool_dispatch_t *dispatch) override; |
| 44 | 44 | ||
| 45 | public: // vm_scheduler_control_t | 45 | public: // vm_scheduler_control_t |
| 46 | void enqueue_blocked_thread(uint32_t thread_id) override final; | 46 | void enqueue_blocked_thread(uint32_t thread_id) override; |
| 47 | 47 | ||
| 48 | std::vector<std::shared_ptr<const vm_thread_t>> get_all_threads() const override final; | 48 | std::vector<std::shared_ptr<const vm_thread_t>> get_all_threads() const override; |
| 49 | 49 | ||
| 50 | private: | 50 | private: |
| 51 | struct thread_instance_t final | 51 | struct thread_instance_t final |
| @@ -50,7 +50,7 @@ std::size_t vm_tool_dispatch_t::load_tool(std::shared_ptr<runtime::vm_tool_t> to · 49 unmodified lines | |||
| 50 | { | 50 | { |
| 51 | // [TODO] For debug builds, it might be useful to wrap the controller in a proxy that records | 51 | // [TODO] For debug builds, it might be useful to wrap the controller in a proxy that records |
| 52 | // the subscribed callbacks and asserts they are all properly unsubscribed during agent unload. | 52 | // the subscribed callbacks and asserts they are all properly unsubscribed during agent unload. |
| 53 | tool->on_load(_vm, *this, current_id); | 53 | tool->on_load(*this, current_id); |
| 54 | } | 54 | } |
| 55 | catch (...) | 55 | catch (...) |
| 56 | { | 56 | { |
| @@ -91,12 +91,12 @@ std::size_t vm_tool_dispatch_t::unload_tool(std::size_t tool_id) · 34 unmodified lines | |||
| 91 | opcode_t vm_tool_dispatch_t::on_breakpoint(vm_registers_t &r) | 91 | opcode_t vm_tool_dispatch_t::on_breakpoint(vm_registers_t &r) |
| 92 | { | 92 | { |
| 93 | opcode_t original_op; | 93 | opcode_t original_op; |
| 94 | std::size_t cookie_id; | 94 | cookie_t cookie_id; |
| 95 | std::tie(original_op, cookie_id) = get_original_opcode(r); | 95 | std::tie(original_op, cookie_id) = get_original_opcode_and_cookie(r); |
| 96 | 96 | ||
| 97 | vm_event_context_t cxt{}; | 97 | vm_event_context_t cxt{}; |
| 98 | cxt.value1.registers = &r; | 98 | cxt.value1.registers = &r; |
| 99 | cxt.value2.i = cookie_id; | 99 | cxt.value2.cookie = cookie_id; |
| 100 | 100 | ||
| 101 | _events.fire_event_callbacks(vm_event_t::breakpoint, cxt); | 101 | _events.fire_event_callbacks(vm_event_t::breakpoint, cxt); |
| 102 | 102 | ||
| @@ -123,7 +123,7 @@ void vm_tool_dispatch_t::on_exception_unhandled(vm_registers_t &r, const vm_stri · 20 unmodified lines | |||
| 123 | _events.fire_event_callbacks(vm_event_t::exception_unhandled, cxt); | 123 | _events.fire_event_callbacks(vm_event_t::exception_unhandled, cxt); |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | void vm_tool_dispatch_t::on_module_load(std::shared_ptr<vm_module_t> &m, const vm_string_t &path) | 126 | void vm_tool_dispatch_t::on_module_vm_load(std::shared_ptr<vm_module_t> &m, const vm_string_t &path) |
| 127 | { | 127 | { |
| 128 | assert(m != nullptr); | 128 | assert(m != nullptr); |
| 129 | 129 | ||
| @@ -131,7 +131,18 @@ void vm_tool_dispatch_t::on_module_load(std::shared_ptr<vm_module_t> &m, const v · 1 unmodified line | |||
| 131 | cxt.value1.module = &m; | 131 | cxt.value1.module = &m; |
| 132 | cxt.value2.str = &path; | 132 | cxt.value2.str = &path; |
| 133 | 133 | ||
| 134 | _events.fire_event_callbacks(vm_event_t::module_load, cxt); | 134 | _events.fire_event_callbacks(vm_event_t::module_vm_load, cxt); |
| 135 | } | ||
| 136 | |||
| 137 | void vm_tool_dispatch_t::on_module_thread_load(vm_registers_t &r, std::shared_ptr<vm_module_t> &m) | ||
| 138 | { | ||
| 139 | assert(m != nullptr); | ||
| 140 | |||
| 141 | vm_event_context_t cxt{}; | ||
| 142 | cxt.value1.registers = &r; | ||
| 143 | cxt.value2.module = &m; | ||
| 144 | |||
| 145 | _events.fire_event_callbacks(vm_event_t::module_thread_load, cxt); | ||
| 135 | } | 146 | } |
| 136 | 147 | ||
| 137 | void vm_tool_dispatch_t::on_thread_begin(vm_thread_t &t) | 148 | void vm_tool_dispatch_t::on_thread_begin(vm_thread_t &t) |
| @@ -150,7 +161,12 @@ void vm_tool_dispatch_t::on_thread_end(vm_thread_t &t) · 12 unmodified lines | |||
| 150 | _events.fire_event_callbacks(vm_event_t::thread_end, cxt); | 161 | _events.fire_event_callbacks(vm_event_t::thread_end, cxt); |
| 151 | } | 162 | } |
| 152 | 163 | ||
| 153 | std::size_t vm_tool_dispatch_t::subscribe_event(vm_event_t evt, vm_event_callback_t cb) | 164 | vm_t& vm_tool_dispatch_t::get_vm_instance() const |
| 165 | { | ||
| 166 | return _vm; | ||
| 167 | } | ||
| 168 | |||
| 169 | cookie_t vm_tool_dispatch_t::subscribe_event(vm_event_t evt, vm_event_callback_t cb) | ||
| 154 | { | 170 | { |
| 155 | if (!cb) | 171 | if (!cb) |
| 156 | throw vm_system_exception{ "Invalid callback for event" }; | 172 | throw vm_system_exception{ "Invalid callback for event" }; |
| @@ -172,7 +188,7 @@ std::size_t vm_tool_dispatch_t::subscribe_event(vm_event_t evt, vm_event_callbac · 15 unmodified lines | |||
| 172 | return cookie_id; | 188 | return cookie_id; |
| 173 | } | 189 | } |
| 174 | 190 | ||
| 175 | void vm_tool_dispatch_t::unsubscribe_event(std::size_t cookie_id) | 191 | void vm_tool_dispatch_t::unsubscribe_event(cookie_t cookie_id) |
| 176 | { | 192 | { |
| 177 | std::lock_guard<std::mutex> lock{ _events.lock }; | 193 | std::lock_guard<std::mutex> lock{ _events.lock }; |
| 178 | 194 | ||
| @@ -190,7 +206,7 @@ void vm_tool_dispatch_t::unsubscribe_event(std::size_t cookie_id) · 11 unmodified lines | |||
| 190 | debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "unsubscribe: event: %d %d", evt, cookie_id); | 206 | debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "unsubscribe: event: %d %d", evt, cookie_id); |
| 191 | } | 207 | } |
| 192 | 208 | ||
| 193 | std::size_t vm_tool_dispatch_t::set_breakpoint(std::shared_ptr<vm_module_t> module, vm_pc_t pc) | 209 | cookie_t vm_tool_dispatch_t::set_breakpoint(std::shared_ptr<vm_module_t> module, vm_pc_t pc) |
| 194 | { | 210 | { |
| 195 | if (module == nullptr || util::has_flag(module->header.runtime_flag, runtime_flags_t::builtin)) | 211 | if (module == nullptr || util::has_flag(module->header.runtime_flag, runtime_flags_t::builtin)) |
| 196 | throw vm_system_exception{ "Unable to set breakpoint in supplied module" }; | 212 | throw vm_system_exception{ "Unable to set breakpoint in supplied module" }; |
| @@ -209,7 +225,7 @@ std::size_t vm_tool_dispatch_t::set_breakpoint(std::shared_ptr<vm_module_t> modu · 12 unmodified lines | |||
| 209 | const auto cookie_id = _breakpoints.cookie_counter++; | 225 | const auto cookie_id = _breakpoints.cookie_counter++; |
| 210 | 226 | ||
| 211 | // Map the cookie to the module/pc pair. | 227 | // Map the cookie to the module/pc pair. |
| 212 | _breakpoints.cookie_to_modulepc[cookie_id] = std::make_pair(module, pc); | 228 | _breakpoints.cookie_to_details[cookie_id] = { module, pc }; |
| 213 | 229 | ||
| 214 | // Record the original opcode | 230 | // Record the original opcode |
| 215 | auto &pc_map = _breakpoints.original_opcodes[reinterpret_cast<std::uintptr_t>(module.get())]; | 231 | auto &pc_map = _breakpoints.original_opcodes[reinterpret_cast<std::uintptr_t>(module.get())]; |
| @@ -225,20 +241,30 @@ std::size_t vm_tool_dispatch_t::set_breakpoint(std::shared_ptr<vm_module_t> modu · 9 unmodified lines | |||
| 225 | } | 241 | } |
| 226 | } | 242 | } |
| 227 | 243 | ||
| 228 | void vm_tool_dispatch_t::clear_breakpoint(std::size_t cookie_id) | 244 | breakpoint_details_t vm_tool_dispatch_t::get_breakpoint_details(cookie_t cookie_id) const |
| 229 | { | 245 | { |
| 230 | std::lock_guard<std::mutex> lock{ _breakpoints.lock }; | 246 | std::lock_guard<std::mutex> lock{ _breakpoints.lock }; |
| 231 | 247 | ||
| 232 | // Resolve the cookie to the module/pc pair | 248 | auto iter_cookie = _breakpoints.cookie_to_details.find(cookie_id); |
| 233 | auto iter_cookie = _breakpoints.cookie_to_modulepc.find(cookie_id); | 249 | if (iter_cookie == _breakpoints.cookie_to_details.cend()) |
| 234 | if (iter_cookie == _breakpoints.cookie_to_modulepc.cend()) | 250 | throw vm_system_exception{ "Unknown breakpoint cookie" }; |
| 235 | return; | 251 | |
| 252 | return iter_cookie->second; | ||
| 253 | } | ||
| 254 | |||
| 255 | void vm_tool_dispatch_t::clear_breakpoint(cookie_t cookie_id) | ||
| 256 | { | ||
| 257 | std::lock_guard<std::mutex> lock{ _breakpoints.lock }; | ||
| 258 | |||
| 259 | auto iter_cookie = _breakpoints.cookie_to_details.find(cookie_id); | ||
| 260 | if (iter_cookie == _breakpoints.cookie_to_details.cend()) | ||
| 261 | throw vm_system_exception{ "Unknown breakpoint cookie" }; | ||
| 236 | 262 | ||
| 237 | auto mod_pc_pair = iter_cookie->second; | 263 | auto details = iter_cookie->second; |
| 238 | const auto target_pc = mod_pc_pair.second; | 264 | const auto target_pc = details.pc; |
| 239 | 265 | ||
| 240 | // Determine the original opcode | 266 | // Determine the original opcode |
| 241 | auto iter_orig = _breakpoints.original_opcodes.find(reinterpret_cast<std::uintptr_t>(mod_pc_pair.first.get())); | 267 | auto iter_orig = _breakpoints.original_opcodes.find(reinterpret_cast<std::uintptr_t>(details.module.get())); |
| 242 | assert(iter_orig != _breakpoints.original_opcodes.cend()); | 268 | assert(iter_orig != _breakpoints.original_opcodes.cend()); |
| 243 | 269 | ||
| 244 | auto &pc_map = iter_orig->second; | 270 | auto &pc_map = iter_orig->second; |
| @@ -248,12 +274,12 @@ void vm_tool_dispatch_t::clear_breakpoint(std::size_t cookie_id) · 3 unmodified lines | |||
| 248 | auto original_opcode = iter_pc->second.first; | 274 | auto original_opcode = iter_pc->second.first; |
| 249 | 275 | ||
| 250 | // Replace the breakpoint opcode with the original | 276 | // Replace the breakpoint opcode with the original |
| 251 | auto &code_section = const_cast<code_section_t &>(mod_pc_pair.first->code_section); | 277 | auto &code_section = const_cast<code_section_t &>(details.module->code_section); |
| 252 | assert(code_section[target_pc].op.opcode == opcode_t::brkpt); | 278 | assert(code_section[target_pc].op.opcode == opcode_t::brkpt); |
| 253 | code_section[target_pc].op.opcode = original_opcode; | 279 | code_section[target_pc].op.opcode = original_opcode; |
| 254 | 280 | ||
| 255 | if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>()) | 281 | if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>()) |
| 256 | debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "breakpoint: unset: %d %d >>%s<<", cookie_id, target_pc, mod_pc_pair.first->module_name->str()); | 282 | debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "breakpoint: unset: %d %d >>%s<<", cookie_id, target_pc, details.module->module_name->str()); |
| 257 | 283 | ||
| 258 | // Clean-up the PC mapping to opcode | 284 | // Clean-up the PC mapping to opcode |
| 259 | pc_map.erase(iter_pc); | 285 | pc_map.erase(iter_pc); |
| @@ -263,10 +289,10 @@ void vm_tool_dispatch_t::clear_breakpoint(std::size_t cookie_id) · 3 unmodified lines | |||
| 263 | _breakpoints.original_opcodes.erase(iter_orig); | 289 | _breakpoints.original_opcodes.erase(iter_orig); |
| 264 | 290 | ||
| 265 | // Remove the cookie map | 291 | // Remove the cookie map |
| 266 | _breakpoints.cookie_to_modulepc.erase(iter_cookie); | 292 | _breakpoints.cookie_to_details.erase(iter_cookie); |
| 267 | } | 293 | } |
| 268 | 294 | ||
| 269 | std::tuple<opcode_t, std::size_t> vm_tool_dispatch_t::get_original_opcode(const vm_registers_t &r) | 295 | std::tuple<opcode_t, cookie_t> vm_tool_dispatch_t::get_original_opcode_and_cookie(const vm_registers_t &r) |
| 270 | { | 296 | { |
| 271 | const auto module = r.module_ref->module.get(); | 297 | const auto module = r.module_ref->module.get(); |
| 272 | const auto current_pc = r.pc - 1; | 298 | const auto current_pc = r.pc - 1; |
| @@ -42,8 +42,11 @@ namespace disvm · 41 unmodified lines | |||
| 42 | // Callback on an unhandled exception | 42 | // Callback on an unhandled exception |
| 43 | void on_exception_unhandled(vm_registers_t &r, const vm_string_t &id, vm_alloc_t &e); | 43 | void on_exception_unhandled(vm_registers_t &r, const vm_string_t &id, vm_alloc_t &e); |
| 44 | 44 | ||
| 45 | // Callback on module load | 45 | // Callback for when the VM loads a module from disk |
| 46 | void on_module_load(std::shared_ptr<vm_module_t> &m, const vm_string_t &path); | 46 | void on_module_vm_load(std::shared_ptr<vm_module_t> &m, const vm_string_t &path); |
| 47 | |||
| 48 | // Callback for when a thread loads a module into its scope | ||
| 49 | void on_module_thread_load(vm_registers_t &r, std::shared_ptr<vm_module_t> &m); | ||
| 47 | 50 | ||
| 48 | // Callback when thread lifetime begins. | 51 | // Callback when thread lifetime begins. |
| 49 | // The thread has not been scheduled and no opcodes have been executed. | 52 | // The thread has not been scheduled and no opcodes have been executed. |
| @@ -53,14 +56,17 @@ namespace disvm · 3 unmodified lines | |||
| 53 | void on_thread_end(vm_thread_t &t); | 56 | void on_thread_end(vm_thread_t &t); |
| 54 | 57 | ||
| 55 | public: // vm_tool_controller_t | 58 | public: // vm_tool_controller_t |
| 56 | std::size_t subscribe_event(vm_event_t evt, vm_event_callback_t cb) override final; | 59 | vm_t& get_vm_instance() const override; |
| 57 | void unsubscribe_event(std::size_t cookie_id) override final; | 60 | |
| 61 | cookie_t subscribe_event(vm_event_t evt, vm_event_callback_t cb) override; | ||
| 62 | void unsubscribe_event(cookie_t cookie_id) override; | ||
| 58 | 63 | ||
| 59 | std::size_t set_breakpoint(std::shared_ptr<vm_module_t> module, vm_pc_t pc) override final; | 64 | cookie_t set_breakpoint(std::shared_ptr<vm_module_t> module, vm_pc_t pc) override; |
| 60 | void clear_breakpoint(std::size_t cookie_id) override final; | 65 | breakpoint_details_t get_breakpoint_details(cookie_t) const override; |
| 66 | void clear_breakpoint(cookie_t cookie_id) override; | ||
| 61 | 67 | ||
| 62 | private: | 68 | private: |
| 63 | std::tuple<opcode_t, std::size_t> get_original_opcode(const vm_registers_t &r); | 69 | std::tuple<opcode_t, cookie_t> get_original_opcode_and_cookie(const vm_registers_t &r); |
| 64 | 70 | ||
| 65 | private: | 71 | private: |
| 66 | vm_t &_vm; | 72 | vm_t &_vm; |
| @@ -73,28 +79,28 @@ namespace disvm · 6 unmodified lines | |||
| 73 | { | 79 | { |
| 74 | void fire_event_callbacks(vm_event_t, vm_event_context_t &); | 80 | void fire_event_callbacks(vm_event_t, vm_event_context_t &); |
| 75 | 81 | ||
| 76 | using cookie_to_callback_map_t = std::unordered_map<std::size_t, vm_event_callback_t>; | 82 | using cookie_to_callback_map_t = std::unordered_map<cookie_t, vm_event_callback_t>; |
| 77 | using event_to_callbacks_map_t = std::unordered_map<vm_event_t, cookie_to_callback_map_t>; | 83 | using event_to_callbacks_map_t = std::unordered_map<vm_event_t, cookie_to_callback_map_t>; |
| 78 | event_to_callbacks_map_t callbacks; | 84 | event_to_callbacks_map_t callbacks; |
| 79 | 85 | ||
| 80 | using cookie_to_event_map_t = std::unordered_map<std::size_t, vm_event_t>; | 86 | using cookie_to_event_map_t = std::unordered_map<cookie_t, vm_event_t>; |
| 81 | cookie_to_event_map_t cookie_to_event; | 87 | cookie_to_event_map_t cookie_to_event; |
| 82 | 88 | ||
| 83 | std::mutex lock; | 89 | mutable std::mutex lock; |
| 84 | std::size_t cookie_counter; | 90 | cookie_t cookie_counter; |
| 85 | } _events; | 91 | } _events; |
| 86 | 92 | ||
| 87 | struct | 93 | struct |
| 88 | { | 94 | { |
| 89 | using cookie_to_modulepc_t = std::unordered_map<std::size_t, std::pair<std::shared_ptr<const vm_module_t>, vm_pc_t>>; | 95 | using cookie_to_details_t = std::unordered_map<cookie_t, breakpoint_details_t>; |
| 90 | cookie_to_modulepc_t cookie_to_modulepc; | 96 | cookie_to_details_t cookie_to_details; |
| 91 | 97 | ||
| 92 | using pc_to_opcode_cookie_map_t = std::unordered_map<vm_pc_t, std::pair<opcode_t, std::size_t>>; | 98 | using pc_to_opcode_cookie_map_t = std::unordered_map<vm_pc_t, std::pair<opcode_t, cookie_t>>; |
| 93 | using module_to_pc_map_t = std::unordered_map<std::uintptr_t, pc_to_opcode_cookie_map_t>; | 99 | using module_to_pc_map_t = std::unordered_map<std::uintptr_t, pc_to_opcode_cookie_map_t>; |
| 94 | module_to_pc_map_t original_opcodes; | 100 | module_to_pc_map_t original_opcodes; |
| 95 | 101 | ||
| 96 | std::mutex lock; | 102 | mutable std::mutex lock; |
| 97 | std::size_t cookie_counter; | 103 | cookie_t cookie_counter; |
| 98 | } _breakpoints; | 104 | } _breakpoints; |
| 99 | }; | 105 | }; |
| 100 | } | 106 | } |
| @@ -126,53 +126,63 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 125 unmodified lines | |||
| 126 | if (path == nullptr) | 126 | if (path == nullptr) |
| 127 | throw vm_user_exception{ "Invalid module path" }; | 127 | throw vm_user_exception{ "Invalid module path" }; |
| 128 | 128 | ||
| 129 | std::lock_guard<std::mutex> lock{ _modules_lock }; | 129 | auto new_module_iter = loaded_modules_t::const_iterator{}; |
| 130 | 130 | auto new_module = std::shared_ptr<vm_module_t>{}; | |
| 131 | // Check if the module is already loaded. | ||
| 132 | auto iter = std::begin(_modules); | ||
| 133 | while (iter != std::end(_modules)) | ||
| 134 | { | 131 | { |
| 135 | if (0 == ::strcmp(iter->origin->str(), path)) | 132 | std::lock_guard<std::mutex> lock{ _modules_lock }; |
| 136 | { | ||
| 137 | auto module = iter->module.lock(); | ||
| 138 | 133 | ||
| 139 | // If an entry exists but is null, the module was loaded before. Read the module again and return. | 134 | // Check if the module is already loaded. |
| 140 | if (module == nullptr) | 135 | auto iter = std::begin(_modules); |
| 136 | while (iter != std::end(_modules)) | ||
| 137 | { | ||
| 138 | if (0 == ::strcmp(iter->origin->str(), path)) | ||
| 141 | { | 139 | { |
| 142 | module = std::shared_ptr<vm_module_t>{ std::move(read_module(path)) }; | 140 | auto module = iter->module.lock(); |
| 143 | iter->module = module; | 141 | |
| 142 | // If an entry exists but is null, the module was loaded before. Read the module again and return. | ||
| 143 | if (module == nullptr) | ||
| 144 | { | ||
| 145 | module = std::shared_ptr<vm_module_t>{ std::move(read_module(path)) }; | ||
| 146 | iter->module = module; | ||
| 144 | 147 | ||
| 145 | debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "reload: vm module: >>%s<<\n", path); | 148 | debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "reload: vm module: >>%s<<\n", path); |
| 149 | } | ||
| 150 | |||
| 151 | return module; | ||
| 146 | } | 152 | } |
| 147 | 153 | ||
| 148 | return module; | 154 | ++iter; |
| 149 | } | 155 | } |
| 150 | 156 | ||
| 151 | ++iter; | 157 | if (path[0] == BUILTIN_MODULE_PREFIX_CHAR) |
| 152 | } | 158 | { |
| 159 | new_module = std::move(builtin::get_builtin_module(path)); | ||
| 160 | } | ||
| 161 | else | ||
| 162 | { | ||
| 163 | // Read in the module | ||
| 164 | new_module = std::move(read_module(path)); | ||
| 165 | } | ||
| 153 | 166 | ||
| 154 | auto new_module = std::shared_ptr<vm_module_t>{}; | 167 | auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path)); |
| 155 | if (path[0] == BUILTIN_MODULE_PREFIX_CHAR) | 168 | |
| 156 | { | 169 | auto load_id_next = std::size_t{ 0 }; |
| 157 | new_module = std::move(builtin::get_builtin_module(path)); | 170 | if (!_modules.empty()) |
| 158 | } | 171 | load_id_next = (_modules.front().load_id + 1); |
| 159 | else | 172 | |
| 160 | { | 173 | _modules.push_front(std::move(loaded_vm_module_t{ load_id_next, std::move(path_local), new_module })); |
| 161 | // Read in the module | 174 | new_module_iter = _modules.begin(); |
| 162 | new_module = std::move(read_module(path)); | ||
| 163 | } | 175 | } |
| 164 | 176 | ||
| 165 | auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path)); | ||
| 166 | if (_tool_dispatch != nullptr) | 177 | if (_tool_dispatch != nullptr) |
| 167 | { | 178 | { |
| 168 | std::lock_guard<std::mutex> lock{ _tool_dispatch_lock }; | 179 | std::lock_guard<std::mutex> lock{ _tool_dispatch_lock }; |
| 169 | if (_tool_dispatch != nullptr) | 180 | if (_tool_dispatch != nullptr) |
| 170 | _tool_dispatch->on_module_load(new_module, *path_local); | 181 | _tool_dispatch->on_module_vm_load(new_module, *new_module_iter->origin); |
| 171 | } | 182 | } |
| 172 | 183 | ||
| 173 | _modules.push_front(std::move(loaded_vm_module_t{ std::move(path_local), new_module })); | 184 | if (debug::is_component_tracing_enabled<debug::component_trace_t::module>()) |
| 174 | 185 | debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "load: vm module: >>%s<<\n", path); | |
| 175 | debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "load: vm module: >>%s<<\n", path); | ||
| 176 | 186 | ||
| 177 | return new_module; | 187 | return new_module; |
| 178 | } | 188 | } |
| @@ -184,7 +194,10 @@ void vm_t::enum_loaded_modules(loaded_vm_module_callback_t callback) const · 5 unmodified lines | |||
| 184 | 194 | ||
| 185 | std::lock_guard<std::mutex> lock{ _modules_lock }; | 195 | std::lock_guard<std::mutex> lock{ _modules_lock }; |
| 186 | for (auto &m : _modules) | 196 | for (auto &m : _modules) |
| 187 | callback(m); | 197 | { |
| 198 | if (!callback(m)) | ||
| 199 | return; | ||
| 200 | } | ||
| 188 | } | 201 | } |
| 189 | 202 | ||
| 190 | runtime::vm_scheduler_control_t &vm_t::get_scheduler_control() const | 203 | runtime::vm_scheduler_control_t &vm_t::get_scheduler_control() const |
| @@ -170,13 +170,13 @@ void disvm::runtime::init_memory(const type_descriptor_t &type_desc, void *data) · 169 unmodified lines | |||
| 170 | 170 | ||
| 171 | namespace | 171 | namespace |
| 172 | { | 172 | { |
| 173 | void dec_ref_and_free_pointer_field(pointer_t pointer_field) | 173 | void dec_ref_and_free_pointer_field(pointer_t pointer_field, std::size_t) |
| 174 | { | 174 | { |
| 175 | assert(pointer_field != nullptr); | 175 | assert(pointer_field != nullptr); |
| 176 | dec_ref_count_and_free(vm_alloc_t::from_allocation(pointer_field)); | 176 | dec_ref_count_and_free(vm_alloc_t::from_allocation(pointer_field)); |
| 177 | } | 177 | } |
| 178 | 178 | ||
| 179 | void add_ref_pointer_field(pointer_t pointer_field) | 179 | void add_ref_pointer_field(pointer_t pointer_field, std::size_t) |
| 180 | { | 180 | { |
| 181 | assert(pointer_field != nullptr); | 181 | assert(pointer_field != nullptr); |
| 182 | vm_alloc_t::from_allocation(pointer_field)->add_ref(); | 182 | vm_alloc_t::from_allocation(pointer_field)->add_ref(); |