Unified · Split
| @@ -11,7 +11,7 @@ · 10 unmodified lines | |||
| 11 | #include <string> | 11 | #include <string> |
| 12 | #include <algorithm> | 12 | #include <algorithm> |
| 13 | #include <map> | 13 | #include <map> |
| 14 | #include <unordered_set> | 14 | #include <set> |
| 15 | #include <utils.h> | 15 | #include <utils.h> |
| 16 | #include <vm_memory.h> | 16 | #include <vm_memory.h> |
| 17 | #include <vm_asm.h> | 17 | #include <vm_asm.h> |
| @@ -91,28 +91,31 @@ namespace · 73 unmodified lines | |||
| 91 | { | 91 | { |
| 92 | static std::string last_successful_cmd_option; | 92 | static std::string last_successful_cmd_option; |
| 93 | 93 | ||
| 94 | dbg_cmd_cxt_t(debugger &d, const vm_registers_t &r) | 94 | dbg_cmd_cxt_t(debugger &d, vm_registers_t &r, bool transient_context = false) |
| 95 | : dbg{ d } | 95 | : dbg{ d } |
| 96 | , exit_break{ false } | 96 | , exit_break{ false } |
| 97 | , current_register{ &r } | ||
| 97 | , initial_register{ r } | 98 | , initial_register{ r } |
| 99 | , transient_context{ transient_context } | ||
| 98 | , vm{ d.controller->get_vm_instance() } | 100 | , vm{ d.controller->get_vm_instance() } |
| 99 | { | 101 | { |
| 100 | current_register = &initial_register; | ||
| 101 | last_successful_cmd = dbg.get_option(last_successful_cmd_option); | 102 | last_successful_cmd = dbg.get_option(last_successful_cmd_option); |
| 102 | } | 103 | } |
| 103 | 104 | ||
| 104 | ~dbg_cmd_cxt_t() | 105 | ~dbg_cmd_cxt_t() |
| 105 | { | 106 | { |
| 106 | dbg.set_option(last_successful_cmd_option, std::move(last_successful_cmd)); | 107 | if (!transient_context) |
| 108 | dbg.set_option(last_successful_cmd_option, std::move(last_successful_cmd)); | ||
| 107 | } | 109 | } |
| 108 | 110 | ||
| 111 | const bool transient_context; | ||
| 109 | bool exit_break; | 112 | bool exit_break; |
| 110 | std::string last_successful_cmd; | 113 | std::string last_successful_cmd; |
| 111 | 114 | ||
| 112 | debugger &dbg; | 115 | debugger &dbg; |
| 113 | const vm_t &vm; | 116 | const vm_t &vm; |
| 114 | const vm_registers_t *current_register; | 117 | const vm_registers_t *current_register; |
| 115 | const vm_registers_t &initial_register; | 118 | vm_registers_t &initial_register; |
| 116 | }; | 119 | }; |
| 117 | 120 | ||
| 118 | std::string dbg_cmd_cxt_t::last_successful_cmd_option = "last_successful_cmd"; | 121 | std::string dbg_cmd_cxt_t::last_successful_cmd_option = "last_successful_cmd"; |
| @@ -168,7 +171,7 @@ namespace · 49 unmodified lines | |||
| 168 | 171 | ||
| 169 | if (!r.module_ref->is_builtin_module()) | 172 | if (!r.module_ref->is_builtin_module()) |
| 170 | { | 173 | { |
| 171 | assert(r.pc < static_cast<word_t>(r.module_ref->code_section.size())); | 174 | assert(static_cast<std::size_t>(r.pc) < r.module_ref->code_section.size()); |
| 172 | const auto &op = r.module_ref->code_section[r.pc].op; | 175 | const auto &op = r.module_ref->code_section[r.pc].op; |
| 173 | register_string | 176 | register_string |
| 174 | << "\n Src: " << op.source | 177 | << "\n Src: " << op.source |
| @@ -332,6 +335,12 @@ namespace · 157 unmodified lines | |||
| 332 | cxt.exit_break = true; | 335 | cxt.exit_break = true; |
| 333 | } | 336 | } |
| 334 | 337 | ||
| 338 | void cmd_step(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) | ||
| 339 | { | ||
| 340 | cxt.dbg.controller->set_thread_trap_flag(*cxt.current_register, vm_trap_flags_t::instruction); | ||
| 341 | cxt.exit_break = true; | ||
| 342 | } | ||
| 343 | |||
| 335 | void cmd_term(const std::vector<std::string> &, dbg_cmd_cxt_t &) | 344 | void cmd_term(const std::vector<std::string> &, dbg_cmd_cxt_t &) |
| 336 | { | 345 | { |
| 337 | throw vm_term_request{}; | 346 | throw vm_term_request{}; |
| @@ -347,7 +356,7 @@ namespace · 9 unmodified lines | |||
| 347 | { | 356 | { |
| 348 | auto msg = std::stringstream{}; | 357 | auto msg = std::stringstream{}; |
| 349 | for (auto t : cxt.vm.get_scheduler_control().get_all_threads()) | 358 | for (auto t : cxt.vm.get_scheduler_control().get_all_threads()) |
| 350 | msg << " " << std::setw(5) << t->get_thread_id() << " - " << t->get_state() << "\n"; | 359 | msg << " " << std::setw(5) << t->get_thread_id() << " - " << t->get_registers().current_thread_state << "\n"; |
| 351 | 360 | ||
| 352 | debug_print_info(msg.str()); | 361 | debug_print_info(msg.str()); |
| 353 | } | 362 | } |
| @@ -843,9 +852,10 @@ namespace · 489 unmodified lines | |||
| 843 | << console_modifiers::reset_all; | 852 | << console_modifiers::reset_all; |
| 844 | } | 853 | } |
| 845 | 854 | ||
| 846 | std::unordered_set<std::string> dbg_options = | 855 | std::set<std::string> dbg_options = |
| 847 | { | 856 | { |
| 848 | { "bp-cmd" } | 857 | { "bp-cmd" }, |
| 858 | { "step-cmd" } | ||
| 849 | }; | 859 | }; |
| 850 | 860 | ||
| 851 | void set_value_cmd(const std::vector<std::string> &args, dbg_cmd_cxt_t &cxt) | 861 | void set_value_cmd(const std::vector<std::string> &args, dbg_cmd_cxt_t &cxt) |
| @@ -889,6 +899,7 @@ namespace · 37 unmodified lines | |||
| 889 | const dbg_cmd_t cmds[] = | 899 | const dbg_cmd_t cmds[] = |
| 890 | { | 900 | { |
| 891 | { "c", "Continue", nullptr, nullptr, cmd_continue }, | 901 | { "c", "Continue", nullptr, nullptr, cmd_continue }, |
| 902 | { "s", "Step over", nullptr, nullptr, cmd_step }, | ||
| 892 | { "term", "Terminate the VM instance", nullptr, nullptr, cmd_term }, | 903 | { "term", "Terminate the VM instance", nullptr, nullptr, cmd_term }, |
| 893 | 904 | ||
| 894 | { "r", "Print registers", nullptr, nullptr, cmd_print_registers }, | 905 | { "r", "Print registers", nullptr, nullptr, cmd_print_registers }, |
| @@ -969,15 +980,19 @@ namespace · 74 unmodified lines | |||
| 969 | } | 980 | } |
| 970 | } | 981 | } |
| 971 | 982 | ||
| 972 | void prompt(debugger &debugger, const vm_registers_t &r) | 983 | void prompt(debugger &debugger, vm_registers_t &r) |
| 973 | { | 984 | { |
| 974 | debugger.controller->suspend_all_threads(); | 985 | debugger.controller->suspend_all_threads(); |
| 975 | 986 | ||
| 976 | std::cout | 987 | if (debugger.first_break) |
| 977 | << console_modifiers::green_font | 988 | { |
| 978 | << console_modifiers::bold | 989 | debugger.first_break = false; |
| 979 | << "'?' for help\n" | 990 | std::cout |
| 980 | << console_modifiers::reset_all; | 991 | << console_modifiers::green_font |
| 992 | << console_modifiers::bold | ||
| 993 | << "'?' for help\n" | ||
| 994 | << console_modifiers::reset_all; | ||
| 995 | } | ||
| 981 | 996 | ||
| 982 | std::string cmd; | 997 | std::string cmd; |
| 983 | 998 | ||
| @@ -1018,6 +1033,7 @@ debugger::debugger(const debugger_options opt) · 34 unmodified lines | |||
| 1018 | : _options{ opt } | 1033 | : _options{ opt } |
| 1019 | , _tool_id{ 0 } | 1034 | , _tool_id{ 0 } |
| 1020 | , controller{ nullptr } | 1035 | , controller{ nullptr } |
| 1036 | , first_break{ true } | ||
| 1021 | { } | 1037 | { } |
| 1022 | 1038 | ||
| 1023 | std::vector<std::string> debugger::resolve_to_function_source_line( | 1039 | std::vector<std::string> debugger::resolve_to_function_source_line( |
| @@ -1203,11 +1219,23 @@ void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id) · 179 unmodified lines | |||
| 1203 | 1219 | ||
| 1204 | debug_print_info(ss.str()); | 1220 | debug_print_info(ss.str()); |
| 1205 | 1221 | ||
| 1206 | auto bp_cmd = get_option({ "bp-cmd" }); | 1222 | auto cmd = get_option({ "bp-cmd" }); |
| 1207 | if (!bp_cmd.empty()) | 1223 | if (!cmd.empty()) |
| 1224 | { | ||
| 1225 | dbg_cmd_cxt_t dbg_cxt{ *this, *cxt.value1.registers, true }; | ||
| 1226 | execute_single_command(dbg_cxt, std::move(cmd)); | ||
| 1227 | } | ||
| 1228 | |||
| 1229 | prompt(*this, *cxt.value1.registers); | ||
| 1230 | })); | ||
| 1231 | |||
| 1232 | _event_cookies.emplace(controller->subscribe_event(vm_event_t::trap, [&](vm_event_t, vm_event_context_t &cxt) | ||
| 1233 | { | ||
| 1234 | auto cmd = get_option({ "step-cmd" }); | ||
| 1235 | if (!cmd.empty()) | ||
| 1208 | { | 1236 | { |
| 1209 | dbg_cmd_cxt_t dbg_cxt{ *this, *cxt.value1.registers }; | 1237 | dbg_cmd_cxt_t dbg_cxt{ *this, *cxt.value1.registers, true }; |
| 1210 | execute_single_command(dbg_cxt, std::move(bp_cmd)); | 1238 | execute_single_command(dbg_cxt, std::move(cmd)); |
| 1211 | } | 1239 | } |
| 1212 | 1240 | ||
| 1213 | prompt(*this, *cxt.value1.registers); | 1241 | prompt(*this, *cxt.value1.registers); |
| @@ -61,6 +61,7 @@ public: // vm_tool_t · 60 unmodified lines | |||
| 61 | public: | 61 | public: |
| 62 | disvm::runtime::vm_tool_controller_t *controller; | 62 | disvm::runtime::vm_tool_controller_t *controller; |
| 63 | std::unordered_set<disvm::runtime::cookie_t> breakpoint_cookies; | 63 | std::unordered_set<disvm::runtime::cookie_t> breakpoint_cookies; |
| 64 | bool first_break; | ||
| 64 | 65 | ||
| 65 | private: | 66 | private: |
| 66 | void load_symbols(const disvm::loaded_vm_module_t &); | 67 | void load_symbols(const disvm::loaded_vm_module_t &); |
| @@ -709,7 +709,7 @@ namespace disvm · 708 unmodified lines | |||
| 709 | const vm_string_t &exception_id); | 709 | const vm_string_t &exception_id); |
| 710 | 710 | ||
| 711 | // VM thread states | 711 | // VM thread states |
| 712 | enum class vm_thread_state_t : uint16_t | 712 | enum class vm_thread_state_t : uint8_t |
| 713 | { | 713 | { |
| 714 | unknown = 0, | 714 | unknown = 0, |
| 715 | 715 | ||
| @@ -729,6 +729,13 @@ namespace disvm · 13 unmodified lines | |||
| 729 | broken, // thread crashed - the scheduler is free to terminate the entire VM if any thread enters this state. | 729 | broken, // thread crashed - the scheduler is free to terminate the entire VM if any thread enters this state. |
| 730 | }; | 730 | }; |
| 731 | 731 | ||
| 732 | // VM trap flags | ||
| 733 | enum class vm_trap_flags_t : uint8_t | ||
| 734 | { | ||
| 735 | none = 0, | ||
| 736 | instruction = 1 << 0, | ||
| 737 | }; | ||
| 738 | |||
| 732 | // Forward declaration | 739 | // Forward declaration |
| 733 | class vm_thread_t; | 740 | class vm_thread_t; |
| 734 | class vm_tool_dispatch_t; | 741 | class vm_tool_dispatch_t; |
| @@ -738,11 +745,11 @@ namespace disvm · 3 unmodified lines | |||
| 738 | { | 745 | { |
| 739 | public: | 746 | public: |
| 740 | vm_registers_t( | 747 | vm_registers_t( |
| 741 | const vm_thread_t &thread, | 748 | vm_thread_t &thread, |
| 742 | vm_module_ref_t &entry); | 749 | vm_module_ref_t &entry); |
| 743 | ~vm_registers_t(); | 750 | ~vm_registers_t(); |
| 744 | 751 | ||
| 745 | const vm_thread_t &thread; | 752 | vm_thread_t &thread; |
| 746 | std::atomic<vm_tool_dispatch_t *> tool_dispatch; | 753 | std::atomic<vm_tool_dispatch_t *> tool_dispatch; |
| 747 | vm_stack_t stack; // Frame pointer access (FP) | 754 | vm_stack_t stack; // Frame pointer access (FP) |
| 748 | vm_pc_t pc; // Current Program counter (Index into module code section) | 755 | vm_pc_t pc; // Current Program counter (Index into module code section) |
| @@ -751,8 +758,9 @@ namespace disvm · 2 unmodified lines | |||
| 751 | vm_module_ref_t *module_ref; // Module reference | 758 | vm_module_ref_t *module_ref; // Module reference |
| 752 | vm_request_mutex_t request_mutex; | 759 | vm_request_mutex_t request_mutex; |
| 753 | 760 | ||
| 754 | vm_thread_state_t current_thread_state; | ||
| 755 | uint16_t current_thread_quanta; | 761 | uint16_t current_thread_quanta; |
| 762 | vm_thread_state_t current_thread_state; | ||
| 763 | vm_trap_flags_t trap_flags; | ||
| 756 | 764 | ||
| 757 | pointer_t src; | 765 | pointer_t src; |
| 758 | pointer_t mid; | 766 | pointer_t mid; |
| @@ -795,8 +803,6 @@ namespace disvm · 36 unmodified lines | |||
| 795 | // this vm thread. | 803 | // this vm thread. |
| 796 | void set_tool_dispatch(vm_tool_dispatch_t *dispatch); | 804 | void set_tool_dispatch(vm_tool_dispatch_t *dispatch); |
| 797 | 805 | ||
| 798 | vm_thread_state_t get_state() const; | ||
| 799 | |||
| 800 | // This function will only return a non-null | 806 | // This function will only return a non-null |
| 801 | // value if this thread is in the broken state. | 807 | // value if this thread is in the broken state. |
| 802 | const char *get_error_message() const; | 808 | const char *get_error_message() const; |
| @@ -23,6 +23,7 @@ namespace disvm · 22 unmodified lines | |||
| 23 | union vm_context_value_t | 23 | union vm_context_value_t |
| 24 | { | 24 | { |
| 25 | cookie_t cookie; | 25 | cookie_t cookie; |
| 26 | vm_trap_flags_t trap; | ||
| 26 | std::shared_ptr<vm_module_t> *module; // Note this is a pointer to std::shared_ptr<> | 27 | std::shared_ptr<vm_module_t> *module; // Note this is a pointer to std::shared_ptr<> |
| 27 | vm_registers_t *registers; | 28 | vm_registers_t *registers; |
| 28 | const vm_thread_t *thread; | 29 | const vm_thread_t *thread; |
| @@ -61,6 +62,9 @@ namespace disvm · 32 unmodified lines | |||
| 61 | 62 | ||
| 62 | breakpoint, // value1: vm_registers_t | 63 | breakpoint, // value1: vm_registers_t |
| 63 | // value2: cookie_t - cookie of breakpoint. '0' indicates the breakpoint was inserted by the VM or compiler. | 64 | // value2: cookie_t - cookie of breakpoint. '0' indicates the breakpoint was inserted by the VM or compiler. |
| 65 | |||
| 66 | trap, // value1: vm_registers_t - The trap flag is unset prior to the event be fired. | ||
| 67 | // value2: vm_trap_flags_t | ||
| 64 | }; | 68 | }; |
| 65 | 69 | ||
| 66 | using vm_event_callback_t = std::function<void(vm_event_t, vm_event_context_t &)>; | 70 | using vm_event_callback_t = std::function<void(vm_event_t, vm_event_context_t &)>; |
| @@ -91,6 +95,7 @@ namespace disvm · 24 unmodified lines | |||
| 91 | // Manipulate thread execution | 95 | // Manipulate thread execution |
| 92 | virtual void suspend_all_threads() = 0; | 96 | virtual void suspend_all_threads() = 0; |
| 93 | virtual void resume_all_threads() = 0; | 97 | virtual void resume_all_threads() = 0; |
| 98 | virtual void set_thread_trap_flag(const vm_registers_t &, vm_trap_flags_t flag) = 0; | ||
| 94 | }; | 99 | }; |
| 95 | } | 100 | } |
| 96 | } | 101 | } |
| @@ -130,7 +130,7 @@ vm_scheduler_control_t &default_scheduler_t::get_controller() const · 129 unmodified lines | |||
| 130 | const vm_thread_t& default_scheduler_t::schedule_thread(std::unique_ptr<runtime::vm_thread_t> thread) | 130 | const vm_thread_t& default_scheduler_t::schedule_thread(std::unique_ptr<runtime::vm_thread_t> thread) |
| 131 | { | 131 | { |
| 132 | assert(thread != nullptr); | 132 | assert(thread != nullptr); |
| 133 | if (thread->get_state() != vm_thread_state_t::ready) | 133 | if (thread->get_registers().current_thread_state != vm_thread_state_t::ready) |
| 134 | throw vm_system_exception{ "Scheduled thread in invalid state" }; | 134 | throw vm_system_exception{ "Scheduled thread in invalid state" }; |
| 135 | 135 | ||
| 136 | if (debug::is_component_tracing_enabled<debug::component_trace_t::scheduler>()) | 136 | if (debug::is_component_tracing_enabled<debug::component_trace_t::scheduler>()) |
| @@ -284,7 +284,7 @@ std::shared_ptr<default_scheduler_t::thread_instance_t> default_scheduler_t::nex · 147 unmodified lines | |||
| 284 | // Check if the passed in thread should be enqueued | 284 | // Check if the passed in thread should be enqueued |
| 285 | if (prev_thread != nullptr) | 285 | if (prev_thread != nullptr) |
| 286 | { | 286 | { |
| 287 | const auto current_state = prev_thread->vm_thread->get_state(); | 287 | const auto current_state = prev_thread->vm_thread->get_registers().current_thread_state; |
| 288 | 288 | ||
| 289 | // This system thread releases ownership of the vm thread after it has been enqueued | 289 | // This system thread releases ownership of the vm thread after it has been enqueued |
| 290 | std::lock_guard<std::mutex> vm_thread_ownership{ prev_thread->system_thread_ownership, std::adopt_lock }; | 290 | std::lock_guard<std::mutex> vm_thread_ownership{ prev_thread->system_thread_ownership, std::adopt_lock }; |
| @@ -367,7 +367,7 @@ void default_scheduler_t::perform_gc(bool is_gc_thread, std::unique_lock<std::mu · 76 unmodified lines | |||
| 367 | for (auto &p : _all_vm_threads) | 367 | for (auto &p : _all_vm_threads) |
| 368 | { | 368 | { |
| 369 | auto &vm_thread = p.second->vm_thread; | 369 | auto &vm_thread = p.second->vm_thread; |
| 370 | if (vm_thread->get_state() != vm_thread_state_t::broken) | 370 | if (vm_thread->get_registers().current_thread_state != vm_thread_state_t::broken) |
| 371 | result.push_back(vm_thread); | 371 | result.push_back(vm_thread); |
| 372 | } | 372 | } |
| 373 | 373 | ||
| @@ -12,6 +12,7 @@ · 11 unmodified lines | |||
| 12 | #include <exceptions.h> | 12 | #include <exceptions.h> |
| 13 | #include <vm_memory.h> | 13 | #include <vm_memory.h> |
| 14 | #include <debug.h> | 14 | #include <debug.h> |
| 15 | #include <utils.h> | ||
| 15 | #include "execution_table.h" | 16 | #include "execution_table.h" |
| 16 | #include "tool_dispatch.h" | 17 | #include "tool_dispatch.h" |
| 17 | 18 | ||
| @@ -51,10 +52,11 @@ namespace · 33 unmodified lines | |||
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | vm_registers_t::vm_registers_t( | 54 | vm_registers_t::vm_registers_t( |
| 54 | const vm_thread_t &thread, | 55 | vm_thread_t &thread, |
| 55 | vm_module_ref_t &entry) | 56 | vm_module_ref_t &entry) |
| 56 | : current_thread_state{ vm_thread_state_t::ready } | 57 | : current_thread_state{ vm_thread_state_t::ready } |
| 57 | , current_thread_quanta{ 0 } | 58 | , current_thread_quanta{ 0 } |
| 59 | , trap_flags{ vm_trap_flags_t::none } | ||
| 58 | , module_ref{ &entry } | 60 | , module_ref{ &entry } |
| 59 | , mp_base{ entry.mp_base } | 61 | , mp_base{ entry.mp_base } |
| 60 | , thread{ thread } | 62 | , thread{ thread } |
| @@ -174,25 +176,37 @@ namespace · 113 unmodified lines | |||
| 174 | { | 176 | { |
| 175 | struct execute_with_tool_t final | 177 | struct execute_with_tool_t final |
| 176 | { | 178 | { |
| 177 | static void begin_exec_loop(vm_registers_t ®isters, vm_t &) | 179 | static void begin_exec_loop(vm_registers_t &r, vm_t &) |
| 178 | { | 180 | { |
| 179 | auto tool_dispatch = registers.tool_dispatch.load(); | 181 | auto tool_dispatch = r.tool_dispatch.load(); |
| 180 | assert(tool_dispatch != nullptr); | 182 | assert(tool_dispatch != nullptr); |
| 181 | 183 | ||
| 182 | tool_dispatch->block_if_thread_suspended(registers.thread.get_thread_id()); | 184 | tool_dispatch->block_if_thread_suspended(r.thread.get_thread_id()); |
| 185 | } | ||
| 186 | |||
| 187 | static void after_exec(vm_registers_t &r, vm_t &) | ||
| 188 | { | ||
| 189 | if (r.trap_flags == vm_trap_flags_t::none | ||
| 190 | || !util::has_flag(r.trap_flags, vm_trap_flags_t::instruction)) | ||
| 191 | return; | ||
| 192 | |||
| 193 | auto tool_dispatch = r.tool_dispatch.load(); | ||
| 194 | assert(tool_dispatch != nullptr); | ||
| 195 | |||
| 196 | tool_dispatch->on_trap(r, vm_trap_flags_t::instruction); | ||
| 183 | } | 197 | } |
| 184 | }; | 198 | }; |
| 185 | 199 | ||
| 186 | struct execute_normal_t final | 200 | struct execute_normal_t final |
| 187 | { | 201 | { |
| 188 | static void begin_exec_loop(vm_registers_t &, vm_t &) { } | 202 | static void begin_exec_loop(vm_registers_t &, vm_t &) { } |
| 203 | static void after_exec(vm_registers_t &, vm_t &) { } | ||
| 189 | }; | 204 | }; |
| 190 | 205 | ||
| 191 | template<typename EXEC_DETOUR> | 206 | template<typename EXEC_DETOUR> |
| 192 | void execute_impl(vm_registers_t &r, vm_t &vm) | 207 | void execute_impl(vm_registers_t &r, vm_t &vm) |
| 193 | { | 208 | { |
| 194 | auto &execution_duration = r.current_thread_quanta; | 209 | for (; 0 != r.current_thread_quanta; --r.current_thread_quanta) |
| 195 | for (; 0 != execution_duration; --execution_duration) | ||
| 196 | { | 210 | { |
| 197 | EXEC_DETOUR::begin_exec_loop(r, vm); | 211 | EXEC_DETOUR::begin_exec_loop(r, vm); |
| 198 | assert(r.stack.peek_frame() != nullptr && "Thread state should not be running with empty stack"); | 212 | assert(r.stack.peek_frame() != nullptr && "Thread state should not be running with empty stack"); |
| @@ -212,6 +226,8 @@ namespace · 13 unmodified lines | |||
| 212 | vm_exec_table[opcode](r, vm); | 226 | vm_exec_table[opcode](r, vm); |
| 213 | r.pc = r.next_pc; | 227 | r.pc = r.next_pc; |
| 214 | 228 | ||
| 229 | EXEC_DETOUR::after_exec(r, vm); | ||
| 230 | |||
| 215 | if (r.current_thread_state != vm_thread_state_t::running) | 231 | if (r.current_thread_state != vm_thread_state_t::running) |
| 216 | break; | 232 | break; |
| 217 | } | 233 | } |
| @@ -306,11 +322,6 @@ void vm_thread_t::set_tool_dispatch(vm_tool_dispatch_t *dispatch) · 88 unmodified lines | |||
| 306 | _registers.tool_dispatch = dispatch; | 322 | _registers.tool_dispatch = dispatch; |
| 307 | } | 323 | } |
| 308 | 324 | ||
| 309 | vm_thread_state_t vm_thread_t::get_state() const | ||
| 310 | { | ||
| 311 | return _registers.current_thread_state; | ||
| 312 | } | ||
| 313 | |||
| 314 | const char *vm_thread_t::get_error_message() const | 325 | const char *vm_thread_t::get_error_message() const |
| 315 | { | 326 | { |
| 316 | return _error_message; | 327 | return _error_message; |
| @@ -329,4 +340,4 @@ uint32_t vm_thread_t::get_parent_thread_id() const · 12 unmodified lines | |||
| 329 | const vm_registers_t& vm_thread_t::get_registers() const | 340 | const vm_registers_t& vm_thread_t::get_registers() const |
| 330 | { | 341 | { |
| 331 | return _registers; | 342 | return _registers; |
| 332 | } | 343 | } |
| @@ -92,6 +92,22 @@ std::size_t vm_tool_dispatch_t::unload_tool(std::size_t tool_id) · 91 unmodified lines | |||
| 92 | return _tools.size(); | 92 | return _tools.size(); |
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | namespace | ||
| 96 | { | ||
| 97 | DEFINE_ENUM_FLAG_OPERATORS(vm_trap_flags_t); | ||
| 98 | } | ||
| 99 | |||
| 100 | void vm_tool_dispatch_t::on_trap(vm_registers_t &r, vm_trap_flags_t f) | ||
| 101 | { | ||
| 102 | vm_event_context_t cxt{}; | ||
| 103 | cxt.value1.registers = &r; | ||
| 104 | cxt.value2.trap = f; | ||
| 105 | |||
| 106 | // Clear the trap flag | ||
| 107 | r.trap_flags &= ~f; | ||
| 108 | _events.fire_event_callbacks(vm_event_t::trap, cxt); | ||
| 109 | } | ||
| 110 | |||
| 95 | opcode_t vm_tool_dispatch_t::on_breakpoint(vm_registers_t &r) | 111 | opcode_t vm_tool_dispatch_t::on_breakpoint(vm_registers_t &r) |
| 96 | { | 112 | { |
| 97 | opcode_t original_op; | 113 | opcode_t original_op; |
| @@ -361,6 +377,15 @@ void vm_tool_dispatch_t::resume_all_threads() · 263 unmodified lines | |||
| 361 | _threads_resume.notify_all(); | 377 | _threads_resume.notify_all(); |
| 362 | } | 378 | } |
| 363 | 379 | ||
| 380 | void vm_tool_dispatch_t::set_thread_trap_flag(const vm_registers_t &r, vm_trap_flags_t flag) | ||
| 381 | { | ||
| 382 | std::lock_guard<std::mutex> suspend_lock{ _threads_resume_lock }; | ||
| 383 | if (!_threads_suspended) | ||
| 384 | throw vm_system_exception{ "Unable to set thread trap flag while threads are not suspended" }; | ||
| 385 | |||
| 386 | const_cast<vm_trap_flags_t &>(r.trap_flags) |= flag; | ||
| 387 | } | ||
| 388 | |||
| 364 | std::tuple<opcode_t, cookie_t> vm_tool_dispatch_t::get_original_opcode_and_cookie(const vm_registers_t &r) | 389 | std::tuple<opcode_t, cookie_t> vm_tool_dispatch_t::get_original_opcode_and_cookie(const vm_registers_t &r) |
| 365 | { | 390 | { |
| 366 | const auto module = r.module_ref->module.get(); | 391 | const auto module = r.module_ref->module.get(); |
| @@ -32,6 +32,9 @@ namespace disvm · 31 unmodified lines | |||
| 32 | // Unloads the tool associated with the supplied ID and returns the current tool count | 32 | // Unloads the tool associated with the supplied ID and returns the current tool count |
| 33 | std::size_t unload_tool(std::size_t tool_id); | 33 | std::size_t unload_tool(std::size_t tool_id); |
| 34 | 34 | ||
| 35 | // Callback for a trap event. | ||
| 36 | void on_trap(vm_registers_t &r, vm_trap_flags_t f); | ||
| 37 | |||
| 35 | // Callback on a breakpoint event. | 38 | // Callback on a breakpoint event. |
| 36 | // Returns opcode that was replaced by breakpoint opcode. | 39 | // Returns opcode that was replaced by breakpoint opcode. |
| 37 | opcode_t on_breakpoint(vm_registers_t &r); | 40 | opcode_t on_breakpoint(vm_registers_t &r); |
| @@ -74,6 +77,7 @@ namespace disvm · 36 unmodified lines | |||
| 74 | 77 | ||
| 75 | void suspend_all_threads() override; | 78 | void suspend_all_threads() override; |
| 76 | void resume_all_threads() override; | 79 | void resume_all_threads() override; |
| 80 | void set_thread_trap_flag(const vm_registers_t &, vm_trap_flags_t flag) override; | ||
| 77 | 81 | ||
| 78 | private: | 82 | private: |
| 79 | std::tuple<opcode_t, cookie_t> get_original_opcode_and_cookie(const vm_registers_t &r); | 83 | std::tuple<opcode_t, cookie_t> get_original_opcode_and_cookie(const vm_registers_t &r); |