History

Debugger supports breakpoints (set/clear/list) Clean up usage of final and override on various classes Distinguish tool events for vm loading module and thread loading module

arr committed 10 years ago

Commit e2f8e251 parent d8c9a3314 files · +382 −177Browse files

Unified · Split

14 files
  1. src/exec/debugger.cpp +191 −61
  2. src/exec/exec.h +11 −6
  3. src/include/disvm.h +4 −1
  4. src/include/runtime.h +4 −5
  5. src/include/vm_memory.h +13 −11
  6. src/include/vm_tools.h +23 −8
  7. src/vm/execution_table.cpp +8 −2
  8. src/vm/garbage_collector.cpp +3 −3
  9. src/vm/garbage_collector.h +3 −3
  10. src/vm/scheduler.h +6 −6
  11. src/vm/tool_dispatch.cpp +48 −22
  12. src/vm/tool_dispatch.h +22 −16
  13. src/vm/vm.cpp +44 −31
  14. src/vm/vm_memory.cpp +2 −2
src/exec/debugger.cpp+191−61
@@ -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 context74 // Debug command context
77 struct dbg_cmd_cxt_t75 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_string127 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 else230 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 else456 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 try465 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 try518 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 try549 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::cout825 std::cout
709 << console_modifiers::green_font826 << 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::cout836 std::cout
@@ -746,12 +862,17 @@ namespace · 25 unmodified lines
746 }862 }
747}863}
748 864
749void debugger::on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t tool_id)865debugger::debugger()
866 : _tool_id{ 0 }
867 , controller{ nullptr }
868{ }
869
870void 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
820void debugger::on_unload()951void 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}
src/exec/exec.h+11−6
@@ -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
14class debugger final : public disvm::runtime::vm_tool_t15class debugger final : public disvm::runtime::vm_tool_t
15{16{
17public:
18 debugger();
19
16public: // vm_tool_t20public: // 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();25public:
26 disvm::runtime::vm_tool_controller_t *controller;
27 std::vector<disvm::runtime::cookie_t> breakpoint_cookies;
20 28
21private:29private:
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_
src/include/disvm.h+4−1
@@ -39,6 +39,8 @@ namespace disvm · 38 unmodified lines
39 class loaded_vm_module_t final39 class loaded_vm_module_t final
40 {40 {
41 public:41 public:
42 std::size_t load_id;
43
42 // Origin of module44 // 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 machine54 // Virtual machine
52 class vm_t final55 class vm_t final
src/include/runtime.h+4−5
@@ -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 interface830 // VM garbage collector interface
828 class vm_garbage_collector_t831 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 VM863 // 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 load867 // Note once the tool has been unloaded the vm and controller references supplied during load
src/include/vm_memory.h+13−11
@@ -70,10 +70,11 @@ namespace disvm · 69 unmodified lines
70 // Increment the ref count of all points in the supplied memory allocation70 // 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 }
src/include/vm_tools.h+23−8
@@ -17,10 +17,12 @@ namespace disvm · 16 unmodified lines
17{17{
18 namespace runtime18 namespace runtime
19 {19 {
20 using cookie_t = std::size_t;
21
20 // Value in event context22 // Value in event context
21 union vm_context_value_t23 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 events41 // List of possible events
40 enum class vm_event_t42 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 - path45 // 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_t50 thread_begin, // value1: const vm_thread_t
46 thread_end, // value1: const vm_thread_t51 thread_end, // value1: const vm_thread_t
@@ -54,22 +59,32 @@ namespace disvm · 7 unmodified lines
54 // value3: const vm_alloc_t - object raised59 // value3: const vm_alloc_t - object raised
55 60
56 breakpoint, // value1: vm_registers_t61 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 controller73 // VM tool controller
63 class vm_tool_controller_t74 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 occurs80 // 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 location84 // 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}
src/vm/execution_table.cpp+8−2
@@ -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 else1761 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
src/vm/garbage_collector.cpp+3−3
@@ -22,7 +22,7 @@ vm_garbage_collector_t::~vm_garbage_collector_t() · 21 unmodified lines
22 22
23std::unique_ptr<vm_garbage_collector_t> disvm::runtime::create_no_op_gc(vm_t &)23std::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_t25 class no_op_gc final : public vm_garbage_collector_t
26 {26 {
27 public:27 public:
28 void track_allocation(vm_alloc_t *) override28 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) -> void121 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 logging157 // If logging is enabled update the callback with logging
158 add_pointer_to_queue = std::function<void(pointer_t p)>{ [&allocs](pointer_t p) -> void158 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);
src/vm/garbage_collector.h+3−3
@@ -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_t28 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;
src/vm/scheduler.h+6−6
@@ -34,18 +34,18 @@ namespace disvm · 33 unmodified lines
34 ~default_scheduler_t();34 ~default_scheduler_t();
35 35
36 public: // vm_scheduler_t36 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_t45 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 final51 struct thread_instance_t final
src/vm/tool_dispatch.cpp+48−22
@@ -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 records51 // [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
91opcode_t vm_tool_dispatch_t::on_breakpoint(vm_registers_t &r)91opcode_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
126void vm_tool_dispatch_t::on_module_load(std::shared_ptr<vm_module_t> &m, const vm_string_t &path)126void 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
137void 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
137void vm_tool_dispatch_t::on_thread_begin(vm_thread_t &t)148void 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
153std::size_t vm_tool_dispatch_t::subscribe_event(vm_event_t evt, vm_event_callback_t cb)164vm_t& vm_tool_dispatch_t::get_vm_instance() const
165{
166 return _vm;
167}
168
169cookie_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
175void vm_tool_dispatch_t::unsubscribe_event(std::size_t cookie_id)191void 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
193std::size_t vm_tool_dispatch_t::set_breakpoint(std::shared_ptr<vm_module_t> module, vm_pc_t pc)209cookie_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 opcode230 // 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
228void vm_tool_dispatch_t::clear_breakpoint(std::size_t cookie_id)244breakpoint_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 pair248 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
255void 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 opcode266 // 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 original276 // 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 opcode284 // 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 map291 // Remove the cookie map
266 _breakpoints.cookie_to_modulepc.erase(iter_cookie);292 _breakpoints.cookie_to_details.erase(iter_cookie);
267}293}
268 294
269std::tuple<opcode_t, std::size_t> vm_tool_dispatch_t::get_original_opcode(const vm_registers_t &r)295std::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;
src/vm/tool_dispatch.h+22−16
@@ -42,8 +42,11 @@ namespace disvm · 41 unmodified lines
42 // Callback on an unhandled exception42 // 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 load45 // 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_t58 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 struct93 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 }
src/vm/vm.cpp+44−31
@@ -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 else172
160 {173 _modules.push_front(std::move(loaded_vm_module_t{ load_id_next, std::move(path_local), new_module }));
161 // Read in the module174 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
190runtime::vm_scheduler_control_t &vm_t::get_scheduler_control() const203runtime::vm_scheduler_control_t &vm_t::get_scheduler_control() const
src/vm/vm_memory.cpp+2−2
@@ -170,13 +170,13 @@ void disvm::runtime::init_memory(const type_descriptor_t &type_desc, void *data) · 169 unmodified lines
170 170
171namespace171namespace
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();