History

Add support for traps in VM Add instruction trap to enable single step scenario in debugger Remote get_state() function from thread

arr committed 9 years ago

Commit cb408a11 parent eb570d98 files · +120 −40Browse files

Unified · Split

8 files
  1. src/exec/debugger.cpp +47 −19
  2. src/exec/exec.h +1 −0
  3. src/include/runtime.h +12 −6
  4. src/include/vm_tools.h +5 −0
  5. src/vm/scheduler.cpp +3 −3
  6. src/vm/thread.cpp +23 −12
  7. src/vm/tool_dispatch.cpp +25 −0
  8. src/vm/tool_dispatch.h +4 −0
src/exec/debugger.cpp+47−19
@@ -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_string176 register_string
174 << "\n Src: " << op.source177 << "\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::cout987 if (debugger.first_break)
977 << console_modifiers::green_font988 {
978 << console_modifiers::bold989 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
1023std::vector<std::string> debugger::resolve_to_function_source_line(1039std::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);
src/exec/exec.h+1−0
@@ -61,6 +61,7 @@ public: // vm_tool_t · 60 unmodified lines
61public:61public:
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
65private:66private:
66 void load_symbols(const disvm::loaded_vm_module_t &);67 void load_symbols(const disvm::loaded_vm_module_t &);
src/include/runtime.h+12−6
@@ -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 states711 // VM thread states
712 enum class vm_thread_state_t : uint16_t712 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 declaration739 // 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 reference758 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-null806 // 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;
src/include/vm_tools.h+5−0
@@ -23,6 +23,7 @@ namespace disvm · 22 unmodified lines
23 union vm_context_value_t23 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_t63 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 execution95 // 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}
src/vm/scheduler.cpp+3−3
@@ -130,7 +130,7 @@ vm_scheduler_control_t &default_scheduler_t::get_controller() const · 129 unmodified lines
130const vm_thread_t& default_scheduler_t::schedule_thread(std::unique_ptr<runtime::vm_thread_t> thread)130const 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 enqueued284 // 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 enqueued289 // 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
src/vm/thread.cpp+23−12
@@ -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
53vm_registers_t::vm_registers_t(54vm_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 final177 struct execute_with_tool_t final
176 {178 {
177 static void begin_exec_loop(vm_registers_t &registers, 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 final200 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
309vm_thread_state_t vm_thread_t::get_state() const
310{
311 return _registers.current_thread_state;
312}
313
314const char *vm_thread_t::get_error_message() const325const 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
329const vm_registers_t& vm_thread_t::get_registers() const340const vm_registers_t& vm_thread_t::get_registers() const
330{341{
331 return _registers;342 return _registers;
332}343}
src/vm/tool_dispatch.cpp+25−0
@@ -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
95namespace
96{
97 DEFINE_ENUM_FLAG_OPERATORS(vm_trap_flags_t);
98}
99
100void 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
95opcode_t vm_tool_dispatch_t::on_breakpoint(vm_registers_t &r)111opcode_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
380void 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
364std::tuple<opcode_t, cookie_t> vm_tool_dispatch_t::get_original_opcode_and_cookie(const vm_registers_t &r)389std::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();
src/vm/tool_dispatch.h+4−0
@@ -32,6 +32,9 @@ namespace disvm · 31 unmodified lines
32 // Unloads the tool associated with the supplied ID and returns the current tool count32 // 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);