Showing the changes to src/vm/tool_dispatch.cpp (1 of 18 files). Show all
Unified · Split
| @@ -5,10 +5,10 @@ · 4 unmodified lines | |||
| 5 | // | 5 | // |
| 6 | 6 | ||
| 7 | #include <cassert> | 7 | #include <cassert> |
| 8 | #include <vector> | ||
| 8 | #include <debug.h> | 9 | #include <debug.h> |
| 9 | #include <vm_tools.h> | 10 | #include <vm_tools.h> |
| 10 | #include <exceptions.h> | 11 | #include <exceptions.h> |
| 11 | #include <unordered_set> | ||
| 12 | #include <utils.h> | 12 | #include <utils.h> |
| 13 | #include "tool_dispatch.h" | 13 | #include "tool_dispatch.h" |
| 14 | 14 | ||
| @@ -88,9 +88,66 @@ std::size_t vm_tool_dispatch_t::unload_tool(std::size_t tool_id) · 73 unmodified lines | |||
| 88 | return _tools.size(); | 88 | return _tools.size(); |
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | opcode_t vm_tool_dispatch_t::on_breakpoint(vm_registers_t &r, vm_t &vm) | 91 | opcode_t vm_tool_dispatch_t::on_breakpoint(vm_registers_t &r) |
| 92 | { | 92 | { |
| 93 | return get_original_opcode(r.module_ref->module.get(), r.pc - 1); | 93 | opcode_t original_op; |
| 94 | std::size_t cookie_id; | ||
| 95 | std::tie(original_op, cookie_id) = get_original_opcode(r); | ||
| 96 | |||
| 97 | vm_event_context_t cxt{}; | ||
| 98 | cxt.value1.registers = &r; | ||
| 99 | cxt.value2.i = cookie_id; | ||
| 100 | |||
| 101 | _events.fire_event_callbacks(vm_event_t::breakpoint, cxt); | ||
| 102 | |||
| 103 | return original_op; | ||
| 104 | } | ||
| 105 | |||
| 106 | void vm_tool_dispatch_t::on_exception_raised(vm_registers_t &r, const vm_string_t &id, vm_alloc_t &e) | ||
| 107 | { | ||
| 108 | vm_event_context_t cxt{}; | ||
| 109 | cxt.value1.registers = &r; | ||
| 110 | cxt.value2.str = &id; | ||
| 111 | cxt.value3.alloc = &e; | ||
| 112 | |||
| 113 | _events.fire_event_callbacks(vm_event_t::exception_raised, cxt); | ||
| 114 | } | ||
| 115 | |||
| 116 | void vm_tool_dispatch_t::on_exception_unhandled(vm_registers_t &r, const vm_string_t &id, vm_alloc_t &e) | ||
| 117 | { | ||
| 118 | vm_event_context_t cxt{}; | ||
| 119 | cxt.value1.registers = &r; | ||
| 120 | cxt.value2.str = &id; | ||
| 121 | cxt.value3.alloc = &e; | ||
| 122 | |||
| 123 | _events.fire_event_callbacks(vm_event_t::exception_unhandled, cxt); | ||
| 124 | } | ||
| 125 | |||
| 126 | void vm_tool_dispatch_t::on_module_load(std::shared_ptr<vm_module_t> &m, const vm_string_t &path) | ||
| 127 | { | ||
| 128 | assert(m != nullptr); | ||
| 129 | |||
| 130 | vm_event_context_t cxt{}; | ||
| 131 | cxt.value1.module = &m; | ||
| 132 | cxt.value2.str = &path; | ||
| 133 | |||
| 134 | _events.fire_event_callbacks(vm_event_t::module_load, cxt); | ||
| 135 | } | ||
| 136 | |||
| 137 | void vm_tool_dispatch_t::on_thread_begin(vm_thread_t &t) | ||
| 138 | { | ||
| 139 | vm_event_context_t cxt{}; | ||
| 140 | cxt.value1.thread = &t; | ||
| 141 | |||
| 142 | _events.fire_event_callbacks(vm_event_t::thread_begin, cxt); | ||
| 143 | } | ||
| 144 | |||
| 145 | void vm_tool_dispatch_t::on_thread_end(vm_thread_t &t) | ||
| 146 | { | ||
| 147 | vm_event_context_t cxt{}; | ||
| 148 | cxt.value1.thread = &t; | ||
| 149 | |||
| 150 | _events.fire_event_callbacks(vm_event_t::thread_end, cxt); | ||
| 94 | } | 151 | } |
| 95 | 152 | ||
| 96 | std::size_t vm_tool_dispatch_t::subscribe_event(vm_event_t evt, vm_event_callback_t cb) | 153 | std::size_t vm_tool_dispatch_t::subscribe_event(vm_event_t evt, vm_event_callback_t cb) |
| @@ -133,15 +190,13 @@ void vm_tool_dispatch_t::unsubscribe_event(std::size_t cookie_id) · 36 unmodified lines | |||
| 133 | debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "unsubscribe: event: %d %d", evt, cookie_id); | 190 | debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "unsubscribe: event: %d %d", evt, cookie_id); |
| 134 | } | 191 | } |
| 135 | 192 | ||
| 136 | std::size_t vm_tool_dispatch_t::set_breakpoint(std::shared_ptr<const vm_module_t> module, vm_pc_t pc) | 193 | std::size_t vm_tool_dispatch_t::set_breakpoint(std::shared_ptr<vm_module_t> module, vm_pc_t pc) |
| 137 | { | 194 | { |
| 138 | if (module == nullptr || util::has_flag(module->header.runtime_flag, runtime_flags_t::builtin)) | 195 | if (module == nullptr || util::has_flag(module->header.runtime_flag, runtime_flags_t::builtin)) |
| 139 | throw vm_system_exception{ "Unable to set breakpoint in supplied module" }; | 196 | throw vm_system_exception{ "Unable to set breakpoint in supplied module" }; |
| 140 | 197 | ||
| 141 | // [TODO] This should be re-considered. Removing 'const' is less than ideal. | 198 | auto &code_section = module->code_section; |
| 142 | // Remove the const value since the code section needs to be updated. | 199 | if (pc >= static_cast<vm_pc_t>(code_section.size())) |
| 143 | auto &code_section = const_cast<code_section_t &>(module->code_section); | ||
| 144 | if (pc >= code_section.size()) | ||
| 145 | throw vm_system_exception{ "Invalid PC for module" }; | 200 | throw vm_system_exception{ "Invalid PC for module" }; |
| 146 | 201 | ||
| 147 | const auto real_opcode = code_section[pc].op.opcode; | 202 | const auto real_opcode = code_section[pc].op.opcode; |
| @@ -158,7 +213,7 @@ std::size_t vm_tool_dispatch_t::set_breakpoint(std::shared_ptr<const vm_module_t · 10 unmodified lines | |||
| 158 | 213 | ||
| 159 | // Record the original opcode | 214 | // Record the original opcode |
| 160 | auto &pc_map = _breakpoints.original_opcodes[reinterpret_cast<std::uintptr_t>(module.get())]; | 215 | auto &pc_map = _breakpoints.original_opcodes[reinterpret_cast<std::uintptr_t>(module.get())]; |
| 161 | pc_map[pc] = real_opcode; | 216 | pc_map[pc] = std::make_pair(real_opcode, cookie_id); |
| 162 | 217 | ||
| 163 | // Replace the current opcode with breakpoint | 218 | // Replace the current opcode with breakpoint |
| 164 | code_section[pc].op.opcode = opcode_t::brkpt; | 219 | code_section[pc].op.opcode = opcode_t::brkpt; |
| @@ -190,7 +245,7 @@ void vm_tool_dispatch_t::clear_breakpoint(std::size_t cookie_id) · 25 unmodified lines | |||
| 190 | auto iter_pc = pc_map.find(target_pc); | 245 | auto iter_pc = pc_map.find(target_pc); |
| 191 | assert(iter_pc != pc_map.cend()); | 246 | assert(iter_pc != pc_map.cend()); |
| 192 | 247 | ||
| 193 | auto original_opcode = iter_pc->second; | 248 | auto original_opcode = iter_pc->second.first; |
| 194 | 249 | ||
| 195 | // Replace the breakpoint opcode with the original | 250 | // Replace the breakpoint opcode with the original |
| 196 | auto &code_section = const_cast<code_section_t &>(mod_pc_pair.first->code_section); | 251 | auto &code_section = const_cast<code_section_t &>(mod_pc_pair.first->code_section); |
| @@ -211,8 +266,11 @@ void vm_tool_dispatch_t::clear_breakpoint(std::size_t cookie_id) · 14 unmodified lines | |||
| 211 | _breakpoints.cookie_to_modulepc.erase(iter_cookie); | 266 | _breakpoints.cookie_to_modulepc.erase(iter_cookie); |
| 212 | } | 267 | } |
| 213 | 268 | ||
| 214 | opcode_t vm_tool_dispatch_t::get_original_opcode(const vm_module_t *module, vm_pc_t pc) | 269 | std::tuple<opcode_t, std::size_t> vm_tool_dispatch_t::get_original_opcode(const vm_registers_t &r) |
| 215 | { | 270 | { |
| 271 | const auto module = r.module_ref->module.get(); | ||
| 272 | const auto current_pc = r.pc - 1; | ||
| 273 | |||
| 216 | if (module == nullptr || util::has_flag(module->header.runtime_flag, runtime_flags_t::builtin)) | 274 | if (module == nullptr || util::has_flag(module->header.runtime_flag, runtime_flags_t::builtin)) |
| 217 | throw vm_system_exception{ "Unable to determine original opcode in supplied module" }; | 275 | throw vm_system_exception{ "Unable to determine original opcode in supplied module" }; |
| 218 | 276 | ||
| @@ -222,11 +280,30 @@ opcode_t vm_tool_dispatch_t::get_original_opcode(const vm_module_t *module, vm_p · 3 unmodified lines | |||
| 222 | if (iter_orig != _breakpoints.original_opcodes.cend()) | 280 | if (iter_orig != _breakpoints.original_opcodes.cend()) |
| 223 | { | 281 | { |
| 224 | auto &pc_map = iter_orig->second; | 282 | auto &pc_map = iter_orig->second; |
| 225 | auto iter_pc = pc_map.find(pc); | 283 | auto iter_pc = pc_map.find(current_pc); |
| 226 | if (iter_pc != pc_map.cend()) | 284 | if (iter_pc != pc_map.cend()) |
| 227 | return iter_pc->second; | 285 | return iter_pc->second; |
| 228 | } | 286 | } |
| 229 | 287 | ||
| 230 | assert(false && "Why are we missing this information?"); | 288 | assert(false && "Why are we missing this information?"); |
| 231 | return opcode_t::runt; // Return NOP instruction? | 289 | return std::make_tuple(opcode_t::runt, 0); // Return NOP instruction? |
| 290 | } | ||
| 291 | |||
| 292 | void vm_tool_dispatch_t::events_t::fire_event_callbacks(vm_event_t event_type, vm_event_context_t &cxt) | ||
| 293 | { | ||
| 294 | auto event_callbacks = std::vector<vm_event_callback_t>{}; | ||
| 295 | |||
| 296 | { | ||
| 297 | std::lock_guard<std::mutex> lock_local{ lock }; | ||
| 298 | |||
| 299 | auto &event_callbacks_tmp = callbacks[event_type]; | ||
| 300 | for (auto &c : event_callbacks_tmp) | ||
| 301 | { | ||
| 302 | assert(static_cast<bool>(c.second) && "Why is callback empty?"); | ||
| 303 | event_callbacks.push_back(c.second); | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | for (auto &c : event_callbacks) | ||
| 308 | c(event_type, cxt); | ||
| 232 | } | 309 | } |