Unified · Split
| @@ -304,7 +304,7 @@ namespace · 303 unmodified lines | ||
| 304 | 304 | |
| 305 | 305 | void cmd_term(const std::vector<std::string> &, dbg_cmd_cxt_t &) |
| 306 | 306 | { |
| 307 | − throw vm_user_exception{ "Debugger signaled VM termination" }; | |
| 307 | + throw vm_term_request{}; | |
| 308 | 308 | } |
| 309 | 309 | |
| 310 | 310 | void cmd_print_registers(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) |
| @@ -128,6 +128,18 @@ namespace disvm · 127 unmodified lines | ||
| 128 | 128 | const char *module_name; |
| 129 | 129 | const vm_pc_t program_counter; |
| 130 | 130 | }; |
| 131 | + | |
| 132 | + class vm_term_request final : public vm_user_exception | |
| 133 | + { | |
| 134 | + public: | |
| 135 | + explicit vm_term_request() | |
| 136 | + : vm_user_exception("VM termination requested") | |
| 137 | + { } | |
| 138 | + | |
| 139 | + explicit vm_term_request(const char *msg) | |
| 140 | + : vm_user_exception(msg) | |
| 141 | + { } | |
| 142 | + }; | |
| 131 | 143 | } |
| 132 | 144 | } |
| 133 | 145 | |
| @@ -717,7 +717,7 @@ namespace disvm · 716 unmodified lines | ||
| 717 | 717 | exiting, // exit instruction |
| 718 | 718 | empty_stack, |
| 719 | 719 | |
| 720 | − broken, // thread crashed | |
| 720 | + broken, // thread crashed - the scheduler is free to terminate the entire VM if any thread enters this state. | |
| 721 | 721 | }; |
| 722 | 722 | |
| 723 | 723 | // Forward declaration |
| @@ -787,6 +787,10 @@ namespace disvm · 63 unmodified lines | ||
| 787 | 787 | |
| 788 | 788 | vm_thread_state_t get_state() const; |
| 789 | 789 | |
| 790 | + // This function will only return a non-null | |
| 791 | + // value if this thread is in the broken state. | |
| 792 | + const char *get_error_message() const; | |
| 793 | + | |
| 790 | 794 | uint32_t get_thread_id() const; |
| 791 | 795 | |
| 792 | 796 | uint32_t get_parent_thread_id() const; |
| @@ -794,9 +798,10 @@ namespace disvm · 1 unmodified line | ||
| 794 | 798 | const vm_registers_t& get_registers() const; |
| 795 | 799 | |
| 796 | 800 | private: |
| 801 | + vm_registers_t _registers; | |
| 797 | 802 | const uint32_t _thread_id; |
| 798 | 803 | const uint32_t _parent_thread_id; |
| 799 | − vm_registers_t _registers; | |
| 804 | + char *_error_message; | |
| 800 | 805 | }; |
| 801 | 806 | |
| 802 | 807 | // |
| @@ -49,6 +49,7 @@ namespace disvm · 48 unmodified lines | ||
| 49 | 49 | |
| 50 | 50 | thread_begin, // value1: const vm_thread_t |
| 51 | 51 | thread_end, // value1: const vm_thread_t |
| 52 | + thread_broken, // value1: const vm_thread_t | |
| 52 | 53 | |
| 53 | 54 | exception_raised, // value1: vm_registers_t |
| 54 | 55 | // value2: const vm_string_t - exception ID |
| @@ -216,8 +216,6 @@ namespace · 215 unmodified lines | ||
| 216 | 216 | if (!success) |
| 217 | 217 | throw module_reader_exception{ "Failed to read magic number" }; |
| 218 | 218 | |
| 219 | − assert(header.magic_number == magic_number_constants::xmagic || header.magic_number == magic_number_constants::smagic); | |
| 220 | − | |
| 221 | 219 | // If the object file is signed, read in the signature. |
| 222 | 220 | if (header.magic_number == magic_number_constants::smagic) |
| 223 | 221 | { |
| @@ -228,10 +226,14 @@ namespace · 4 unmodified lines | ||
| 228 | 226 | const auto bytesRead = reader.get_next_bytes(header.Signature.length, header.Signature.signature.get()); |
| 229 | 227 | if (bytesRead != header.Signature.length) throw module_reader_exception{ "Failed to read full signature" }; |
| 230 | 228 | } |
| 231 | − else | |
| 229 | + else if (header.magic_number == magic_number_constants::xmagic) | |
| 232 | 230 | { |
| 233 | 231 | header.Signature.length = word_t{ 0 }; |
| 234 | 232 | } |
| 233 | + else | |
| 234 | + { | |
| 235 | + throw module_reader_exception{ "Invalid magic number in module" }; | |
| 236 | + } | |
| 235 | 237 | |
| 236 | 238 | auto runtime_flags = operand_t{}; |
| 237 | 239 | std::tie(success, runtime_flags) = read_next_operand(reader); |
| @@ -46,6 +46,13 @@ void default_scheduler_t::worker_main(default_scheduler_t &instance) · 45 unmodified lines | ||
| 46 | 46 | current_thread->vm_thread->execute(instance._vm, instance._vm_thread_quanta); |
| 47 | 47 | } |
| 48 | 48 | } |
| 49 | + catch (const vm_term_request &te) | |
| 50 | + { | |
| 51 | + std::cerr << te.what() << std::endl; | |
| 52 | + | |
| 53 | + instance._terminating = true; | |
| 54 | + instance._worker_event.notify_all(); | |
| 55 | + } | |
| 49 | 56 | catch (const vm_system_exception &se) |
| 50 | 57 | { |
| 51 | 58 | std::stringstream err_msg; |
| @@ -428,12 +435,12 @@ bool default_scheduler_t::enqueue_thread_unsafe(std::shared_ptr<thread_instance_ · 376 unmodified lines | ||
| 428 | 435 | |
| 429 | 436 | case vm_thread_state_t::broken: |
| 430 | 437 | { |
| 431 | − _non_runnable_vm_thread_ids.push_front(thread_id); | |
| 432 | − | |
| 433 | 438 | if (debug::is_component_tracing_enabled<debug::component_trace_t::scheduler>()) |
| 434 | 439 | debug::log_msg(debug::component_trace_t::scheduler, debug::log_level_t::debug, "scheduler: broken: %d\n", thread_id); |
| 435 | 440 | |
| 436 | − break; | |
| 441 | + auto err_msg = thread_instance->vm_thread->get_error_message(); | |
| 442 | + assert(err_msg != nullptr); | |
| 443 | + throw vm_term_request{ err_msg }; | |
| 437 | 444 | } |
| 438 | 445 | |
| 439 | 446 | case vm_thread_state_t::release: |
| @@ -13,7 +13,6 @@ · 12 unmodified lines | ||
| 13 | 13 | #include <condition_variable> |
| 14 | 14 | #include <vector> |
| 15 | 15 | #include <deque> |
| 16 | −#include <forward_list> | |
| 17 | 16 | #include <unordered_set> |
| 18 | 17 | #include <unordered_map> |
| 19 | 18 | #include <atomic> |
| @@ -96,7 +95,6 @@ namespace disvm · 76 unmodified lines | ||
| 96 | 95 | std::size_t _gc_counter; |
| 97 | 96 | |
| 98 | 97 | std::unordered_set<uint32_t> _blocked_vm_thread_ids; |
| 99 | − std::forward_list<uint32_t> _non_runnable_vm_thread_ids; | |
| 100 | 98 | |
| 101 | 99 | using all_thread_map_t = std::unordered_map<uint32_t, std::shared_ptr<thread_instance_t>>; |
| 102 | 100 | all_thread_map_t _all_vm_threads; |
| @@ -111,6 +111,7 @@ vm_thread_t::vm_thread_t( · 110 unmodified lines | ||
| 111 | 111 | vm_module_ref_t &entry, |
| 112 | 112 | uint32_t parent_thread_id) |
| 113 | 113 | : vm_alloc_t(vm_thread_t::type_desc()) |
| 114 | + , _error_message{ nullptr } | |
| 114 | 115 | , _parent_thread_id{ parent_thread_id } |
| 115 | 116 | , _registers{ *this, entry } |
| 116 | 117 | , _thread_id{ get_unique_thread_id() } |
| @@ -135,6 +136,7 @@ vm_thread_t::vm_thread_t( · 18 unmodified lines | ||
| 135 | 136 | const vm_frame_t &initial_frame, |
| 136 | 137 | vm_pc_t start_pc) |
| 137 | 138 | : vm_alloc_t(vm_thread_t::type_desc()) |
| 139 | + , _error_message{ nullptr } | |
| 138 | 140 | , _parent_thread_id{ parent_thread_id } |
| 139 | 141 | , _registers{ *this, entry } |
| 140 | 142 | , _thread_id{ get_unique_thread_id() } |
| @@ -162,6 +164,9 @@ vm_thread_t::~vm_thread_t() · 21 unmodified lines | ||
| 162 | 164 | { |
| 163 | 165 | if (debug::is_component_tracing_enabled<debug::component_trace_t::thread>()) |
| 164 | 166 | debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "destroy: vm thread: %d %d\n", _thread_id, _parent_thread_id); |
| 167 | + | |
| 168 | + free_memory(_error_message); | |
| 169 | + debug::assign_debug_pointer(&_error_message); | |
| 165 | 170 | } |
| 166 | 171 | |
| 167 | 172 | namespace |
| @@ -214,6 +219,7 @@ namespace · 46 unmodified lines | ||
| 214 | 219 | |
| 215 | 220 | vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) |
| 216 | 221 | { |
| 222 | + const auto max_error_message = std::size_t{ 1024 }; | |
| 217 | 223 | _registers.current_thread_state = vm_thread_state_t::running; |
| 218 | 224 | _registers.current_thread_quanta = quanta; |
| 219 | 225 | assert(_registers.pc != runtime_constants::invalid_program_counter); |
| @@ -226,27 +232,27 @@ vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) · 6 unmodified lines | ||
| 226 | 232 | else |
| 227 | 233 | execute_impl<execute_with_tool_t>(_registers, vm); |
| 228 | 234 | } |
| 235 | + catch (const vm_term_request &) | |
| 236 | + { | |
| 237 | + throw; | |
| 238 | + } | |
| 229 | 239 | catch (const unhandled_user_exception &uue) |
| 230 | 240 | { |
| 231 | − // [TODO] Store the exception | |
| 232 | − std::fprintf(stderr, "%s - '%s' in %s at instruction %d\n", uue.what(), uue.exception_id, uue.module_name, uue.program_counter); | |
| 241 | + _error_message = calloc_memory<char>(max_error_message); | |
| 242 | + std::snprintf(_error_message, max_error_message, "%s - '%s' in %s at instruction %d", uue.what(), uue.exception_id, uue.module_name, uue.program_counter); | |
| 233 | 243 | _registers.current_thread_state = vm_thread_state_t::broken; |
| 234 | − if (tool_dispatch != nullptr) | |
| 235 | − tool_dispatch->on_thread_broken(*this); | |
| 236 | 244 | } |
| 237 | 245 | catch (const index_out_of_range_memory &ioor) |
| 238 | 246 | { |
| 239 | − std::fprintf(stderr, "%s - %d [%d,%d]\n", ioor.what(), ioor.invalid_value, ioor.valid_min, ioor.valid_max); | |
| 247 | + _error_message = calloc_memory<char>(max_error_message); | |
| 248 | + std::snprintf(_error_message, max_error_message, "%s - %d [%d,%d]", ioor.what(), ioor.invalid_value, ioor.valid_min, ioor.valid_max); | |
| 240 | 249 | _registers.current_thread_state = vm_thread_state_t::broken; |
| 241 | − if (tool_dispatch != nullptr) | |
| 242 | − tool_dispatch->on_thread_broken(*this); | |
| 243 | 250 | } |
| 244 | 251 | catch (const vm_user_exception &ue) |
| 245 | 252 | { |
| 246 | − std::fprintf(stderr, "%s\n", ue.what()); | |
| 253 | + _error_message = calloc_memory<char>(max_error_message); | |
| 254 | + std::snprintf(_error_message, max_error_message, "%s", ue.what()); | |
| 247 | 255 | _registers.current_thread_state = vm_thread_state_t::broken; |
| 248 | − if (tool_dispatch != nullptr) | |
| 249 | − tool_dispatch->on_thread_broken(*this); | |
| 250 | 256 | } |
| 251 | 257 | |
| 252 | 258 | // Log the execution result |
| @@ -262,16 +268,22 @@ vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) · 9 unmodified lines | ||
| 262 | 268 | _registers.current_thread_state); |
| 263 | 269 | } |
| 264 | 270 | |
| 265 | − // If the thread is still in the running state transition it back to its entry state. | |
| 266 | − if (_registers.current_thread_state == vm_thread_state_t::running) | |
| 271 | + switch (_registers.current_thread_state) | |
| 267 | 272 | { |
| 273 | + case vm_thread_state_t::running: | |
| 268 | 274 | _registers.current_thread_state = (tool_dispatch == nullptr) ? vm_thread_state_t::ready : vm_thread_state_t::debug; |
| 269 | − } | |
| 270 | − else if (vm_thread_state_t::exiting == _registers.current_thread_state | |
| 271 | − || vm_thread_state_t::empty_stack == _registers.current_thread_state) | |
| 272 | − { | |
| 275 | + break; | |
| 276 | + | |
| 277 | + case vm_thread_state_t::empty_stack: | |
| 278 | + case vm_thread_state_t::exiting: | |
| 273 | 279 | if (tool_dispatch != nullptr) |
| 274 | 280 | tool_dispatch->on_thread_end(*this); |
| 281 | + break; | |
| 282 | + | |
| 283 | + case vm_thread_state_t::broken: | |
| 284 | + if (tool_dispatch != nullptr) | |
| 285 | + tool_dispatch->on_thread_broken(*this); | |
| 286 | + break; | |
| 275 | 287 | } |
| 276 | 288 | |
| 277 | 289 | return _registers.current_thread_state; |
| @@ -288,6 +300,11 @@ vm_thread_state_t vm_thread_t::get_state() const · 10 unmodified lines | ||
| 288 | 300 | return _registers.current_thread_state; |
| 289 | 301 | } |
| 290 | 302 | |
| 303 | +const char *vm_thread_t::get_error_message() const | |
| 304 | +{ | |
| 305 | + return _error_message; | |
| 306 | +} | |
| 307 | + | |
| 291 | 308 | uint32_t vm_thread_t::get_thread_id() const |
| 292 | 309 | { |
| 293 | 310 | return _thread_id; |
| @@ -164,8 +164,12 @@ void vm_tool_dispatch_t::on_thread_end(vm_thread_t &t) · 163 unmodified lines | ||
| 164 | 164 | _events.fire_event_callbacks(vm_event_t::thread_end, cxt); |
| 165 | 165 | } |
| 166 | 166 | |
| 167 | −void vm_tool_dispatch_t::on_thread_broken(vm_thread_t &) | |
| 167 | +void vm_tool_dispatch_t::on_thread_broken(vm_thread_t &t) | |
| 168 | 168 | { |
| 169 | + vm_event_context_t cxt{}; | |
| 170 | + cxt.value1.thread = &t; | |
| 171 | + | |
| 172 | + _events.fire_event_callbacks(vm_event_t::thread_broken, cxt); | |
| 169 | 173 | } |
| 170 | 174 | |
| 171 | 175 | void vm_tool_dispatch_t::block_if_thread_suspended(uint32_t thread_id) |