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
1111 #include <string>
1212 #include <algorithm>
1313 #include <map>
14−#include <unordered_set>
14+#include <set>
1515 #include <utils.h>
1616 #include <vm_memory.h>
1717 #include <vm_asm.h>
@@ -91,28 +91,31 @@ namespace · 73 unmodified lines
9191 {
9292 static std::string last_successful_cmd_option;
9393
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)
9595 : dbg{ d }
9696 , exit_break{ false }
97+ , current_register{ &r }
9798 , initial_register{ r }
99+ , transient_context{ transient_context }
98100 , vm{ d.controller->get_vm_instance() }
99101 {
100− current_register = &initial_register;
101102 last_successful_cmd = dbg.get_option(last_successful_cmd_option);
102103 }
103104
104105 ~dbg_cmd_cxt_t()
105106 {
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));
107109 }
108110
111+ const bool transient_context;
109112 bool exit_break;
110113 std::string last_successful_cmd;
111114
112115 debugger &dbg;
113116 const vm_t &vm;
114117 const vm_registers_t *current_register;
115− const vm_registers_t &initial_register;
118+ vm_registers_t &initial_register;
116119 };
117120
118121 std::string dbg_cmd_cxt_t::last_successful_cmd_option = "last_successful_cmd";
@@ -168,7 +171,7 @@ namespace · 49 unmodified lines
168171
169172 if (!r.module_ref->is_builtin_module())
170173 {
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());
172175 const auto &op = r.module_ref->code_section[r.pc].op;
173176 register_string
174177 << "\n Src: " << op.source
@@ -332,6 +335,12 @@ namespace · 157 unmodified lines
332335 cxt.exit_break = true;
333336 }
334337
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+
335344 void cmd_term(const std::vector<std::string> &, dbg_cmd_cxt_t &)
336345 {
337346 throw vm_term_request{};
@@ -347,7 +356,7 @@ namespace · 9 unmodified lines
347356 {
348357 auto msg = std::stringstream{};
349358 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";
351360
352361 debug_print_info(msg.str());
353362 }
@@ -843,9 +852,10 @@ namespace · 489 unmodified lines
843852 << console_modifiers::reset_all;
844853 }
845854
846− std::unordered_set<std::string> dbg_options =
855+ std::set<std::string> dbg_options =
847856 {
848− { "bp-cmd" }
857+ { "bp-cmd" },
858+ { "step-cmd" }
849859 };
850860
851861 void set_value_cmd(const std::vector<std::string> &args, dbg_cmd_cxt_t &cxt)
@@ -889,6 +899,7 @@ namespace · 37 unmodified lines
889899 const dbg_cmd_t cmds[] =
890900 {
891901 { "c", "Continue", nullptr, nullptr, cmd_continue },
902+ { "s", "Step over", nullptr, nullptr, cmd_step },
892903 { "term", "Terminate the VM instance", nullptr, nullptr, cmd_term },
893904
894905 { "r", "Print registers", nullptr, nullptr, cmd_print_registers },
@@ -969,15 +980,19 @@ namespace · 74 unmodified lines
969980 }
970981 }
971982
972− void prompt(debugger &debugger, const vm_registers_t &r)
983+ void prompt(debugger &debugger, vm_registers_t &r)
973984 {
974985 debugger.controller->suspend_all_threads();
975986
976− std::cout
977− << console_modifiers::green_font
978− << console_modifiers::bold
979− << "'?' for help\n"
980− << console_modifiers::reset_all;
987+ if (debugger.first_break)
988+ {
989+ debugger.first_break = false;
990+ std::cout
991+ << console_modifiers::green_font
992+ << console_modifiers::bold
993+ << "'?' for help\n"
994+ << console_modifiers::reset_all;
995+ }
981996
982997 std::string cmd;
983998
@@ -1018,6 +1033,7 @@ debugger::debugger(const debugger_options opt) · 34 unmodified lines
10181033 : _options{ opt }
10191034 , _tool_id{ 0 }
10201035 , controller{ nullptr }
1036+ , first_break{ true }
10211037 { }
10221038
10231039 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
12031219
12041220 debug_print_info(ss.str());
12051221
1206− auto bp_cmd = get_option({ "bp-cmd" });
1207− if (!bp_cmd.empty())
1222+ auto cmd = get_option({ "bp-cmd" });
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())
12081236 {
1209− dbg_cmd_cxt_t dbg_cxt{ *this, *cxt.value1.registers };
1210− execute_single_command(dbg_cxt, std::move(bp_cmd));
1237+ dbg_cmd_cxt_t dbg_cxt{ *this, *cxt.value1.registers, true };
1238+ execute_single_command(dbg_cxt, std::move(cmd));
12111239 }
12121240
12131241 prompt(*this, *cxt.value1.registers);
src/exec/exec.h+1−0
@@ -61,6 +61,7 @@ public: // vm_tool_t · 60 unmodified lines
6161 public:
6262 disvm::runtime::vm_tool_controller_t *controller;
6363 std::unordered_set<disvm::runtime::cookie_t> breakpoint_cookies;
64+ bool first_break;
6465
6566 private:
6667 void load_symbols(const disvm::loaded_vm_module_t &);
src/include/runtime.h+12−6
@@ -709,7 +709,7 @@ namespace disvm · 708 unmodified lines
709709 const vm_string_t &exception_id);
710710
711711 // VM thread states
712− enum class vm_thread_state_t : uint16_t
712+ enum class vm_thread_state_t : uint8_t
713713 {
714714 unknown = 0,
715715
@@ -729,6 +729,13 @@ namespace disvm · 13 unmodified lines
729729 broken, // thread crashed - the scheduler is free to terminate the entire VM if any thread enters this state.
730730 };
731731
732+ // VM trap flags
733+ enum class vm_trap_flags_t : uint8_t
734+ {
735+ none = 0,
736+ instruction = 1 << 0,
737+ };
738+
732739 // Forward declaration
733740 class vm_thread_t;
734741 class vm_tool_dispatch_t;
@@ -738,11 +745,11 @@ namespace disvm · 3 unmodified lines
738745 {
739746 public:
740747 vm_registers_t(
741− const vm_thread_t &thread,
748+ vm_thread_t &thread,
742749 vm_module_ref_t &entry);
743750 ~vm_registers_t();
744751
745− const vm_thread_t &thread;
752+ vm_thread_t &thread;
746753 std::atomic<vm_tool_dispatch_t *> tool_dispatch;
747754 vm_stack_t stack; // Frame pointer access (FP)
748755 vm_pc_t pc; // Current Program counter (Index into module code section)
@@ -751,8 +758,9 @@ namespace disvm · 2 unmodified lines
751758 vm_module_ref_t *module_ref; // Module reference
752759 vm_request_mutex_t request_mutex;
753760
754− vm_thread_state_t current_thread_state;
755761 uint16_t current_thread_quanta;
762+ vm_thread_state_t current_thread_state;
763+ vm_trap_flags_t trap_flags;
756764
757765 pointer_t src;
758766 pointer_t mid;
@@ -795,8 +803,6 @@ namespace disvm · 36 unmodified lines
795803 // this vm thread.
796804 void set_tool_dispatch(vm_tool_dispatch_t *dispatch);
797805
798− vm_thread_state_t get_state() const;
799−
800806 // This function will only return a non-null
801807 // value if this thread is in the broken state.
802808 const char *get_error_message() const;
src/include/vm_tools.h+5−0
@@ -23,6 +23,7 @@ namespace disvm · 22 unmodified lines
2323 union vm_context_value_t
2424 {
2525 cookie_t cookie;
26+ vm_trap_flags_t trap;
2627 std::shared_ptr<vm_module_t> *module; // Note this is a pointer to std::shared_ptr<>
2728 vm_registers_t *registers;
2829 const vm_thread_t *thread;
@@ -61,6 +62,9 @@ namespace disvm · 32 unmodified lines
6162
6263 breakpoint, // value1: vm_registers_t
6364 // 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
6468 };
6569
6670 using vm_event_callback_t = std::function<void(vm_event_t, vm_event_context_t &)>;
@@ -91,6 +95,7 @@ namespace disvm · 24 unmodified lines
9195 // Manipulate thread execution
9296 virtual void suspend_all_threads() = 0;
9397 virtual void resume_all_threads() = 0;
98+ virtual void set_thread_trap_flag(const vm_registers_t &, vm_trap_flags_t flag) = 0;
9499 };
95100 }
96101 }
src/vm/scheduler.cpp+3−3
@@ -130,7 +130,7 @@ vm_scheduler_control_t &default_scheduler_t::get_controller() const · 129 unmodified lines
130130 const vm_thread_t& default_scheduler_t::schedule_thread(std::unique_ptr<runtime::vm_thread_t> thread)
131131 {
132132 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)
134134 throw vm_system_exception{ "Scheduled thread in invalid state" };
135135
136136 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
284284 // Check if the passed in thread should be enqueued
285285 if (prev_thread != nullptr)
286286 {
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;
288288
289289 // This system thread releases ownership of the vm thread after it has been enqueued
290290 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
367367 for (auto &p : _all_vm_threads)
368368 {
369369 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)
371371 result.push_back(vm_thread);
372372 }
373373
src/vm/thread.cpp+23−12
@@ -12,6 +12,7 @@ · 11 unmodified lines
1212 #include <exceptions.h>
1313 #include <vm_memory.h>
1414 #include <debug.h>
15+#include <utils.h>
1516 #include "execution_table.h"
1617 #include "tool_dispatch.h"
1718
@@ -51,10 +52,11 @@ namespace · 33 unmodified lines
5152 }
5253
5354 vm_registers_t::vm_registers_t(
54− const vm_thread_t &thread,
55+ vm_thread_t &thread,
5556 vm_module_ref_t &entry)
5657 : current_thread_state{ vm_thread_state_t::ready }
5758 , current_thread_quanta{ 0 }
59+ , trap_flags{ vm_trap_flags_t::none }
5860 , module_ref{ &entry }
5961 , mp_base{ entry.mp_base }
6062 , thread{ thread }
@@ -174,25 +176,37 @@ namespace · 113 unmodified lines
174176 {
175177 struct execute_with_tool_t final
176178 {
177− static void begin_exec_loop(vm_registers_t &registers, vm_t &)
179+ static void begin_exec_loop(vm_registers_t &r, vm_t &)
178180 {
179− auto tool_dispatch = registers.tool_dispatch.load();
181+ auto tool_dispatch = r.tool_dispatch.load();
180182 assert(tool_dispatch != nullptr);
181183
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);
183197 }
184198 };
185199
186200 struct execute_normal_t final
187201 {
188202 static void begin_exec_loop(vm_registers_t &, vm_t &) { }
203+ static void after_exec(vm_registers_t &, vm_t &) { }
189204 };
190205
191206 template<typename EXEC_DETOUR>
192207 void execute_impl(vm_registers_t &r, vm_t &vm)
193208 {
194− auto &execution_duration = r.current_thread_quanta;
195− for (; 0 != execution_duration; --execution_duration)
209+ for (; 0 != r.current_thread_quanta; --r.current_thread_quanta)
196210 {
197211 EXEC_DETOUR::begin_exec_loop(r, vm);
198212 assert(r.stack.peek_frame() != nullptr && "Thread state should not be running with empty stack");
@@ -212,6 +226,8 @@ namespace · 13 unmodified lines
212226 vm_exec_table[opcode](r, vm);
213227 r.pc = r.next_pc;
214228
229+ EXEC_DETOUR::after_exec(r, vm);
230+
215231 if (r.current_thread_state != vm_thread_state_t::running)
216232 break;
217233 }
@@ -306,11 +322,6 @@ void vm_thread_t::set_tool_dispatch(vm_tool_dispatch_t *dispatch) · 88 unmodified lines
306322 _registers.tool_dispatch = dispatch;
307323 }
308324
309−vm_thread_state_t vm_thread_t::get_state() const
310−{
311− return _registers.current_thread_state;
312−}
313−
314325 const char *vm_thread_t::get_error_message() const
315326 {
316327 return _error_message;
@@ -329,4 +340,4 @@ uint32_t vm_thread_t::get_parent_thread_id() const · 12 unmodified lines
329340 const vm_registers_t& vm_thread_t::get_registers() const
330341 {
331342 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
9292 return _tools.size();
9393 }
9494
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+
95111 opcode_t vm_tool_dispatch_t::on_breakpoint(vm_registers_t &r)
96112 {
97113 opcode_t original_op;
@@ -361,6 +377,15 @@ void vm_tool_dispatch_t::resume_all_threads() · 263 unmodified lines
361377 _threads_resume.notify_all();
362378 }
363379
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+
364389 std::tuple<opcode_t, cookie_t> vm_tool_dispatch_t::get_original_opcode_and_cookie(const vm_registers_t &r)
365390 {
366391 const auto module = r.module_ref->module.get();
src/vm/tool_dispatch.h+4−0
@@ -32,6 +32,9 @@ namespace disvm · 31 unmodified lines
3232 // Unloads the tool associated with the supplied ID and returns the current tool count
3333 std::size_t unload_tool(std::size_t tool_id);
3434
35+ // Callback for a trap event.
36+ void on_trap(vm_registers_t &r, vm_trap_flags_t f);
37+
3538 // Callback on a breakpoint event.
3639 // Returns opcode that was replaced by breakpoint opcode.
3740 opcode_t on_breakpoint(vm_registers_t &r);
@@ -74,6 +77,7 @@ namespace disvm · 36 unmodified lines
7477
7578 void suspend_all_threads() override;
7679 void resume_all_threads() override;
80+ void set_thread_trap_flag(const vm_registers_t &, vm_trap_flags_t flag) override;
7781
7882 private:
7983 std::tuple<opcode_t, cookie_t> get_original_opcode_and_cookie(const vm_registers_t &r);