History

Various work for tool support in the VM. Update some type names (i.e. _t) and alter API signatures to be more consistent.

arr committed 10 years ago

Commit e5d3f861 parent bcb413516 files · +514 −45Browse files

Unified · Split

16 files
  1. src/include/debug.h +1 −0
  2. src/include/disvm.h +7 −3
  3. src/include/opcodes.h +4 −1
  4. src/include/runtime.h +37 −10
  5. src/include/utf8.h +2 −2
  6. src/include/vm_tools.h +63 −0
  7. src/vm/channel.cpp +1 −1
  8. src/vm/disvm.gyp +1 −0
  9. src/vm/execution_table.cpp +26 −5
  10. src/vm/scheduler.cpp +10 −3
  11. src/vm/sys/Sysmod.cpp +3 −3
  12. src/vm/thread.cpp +18 −7
  13. src/vm/tool_dispatch.cpp +232 −0
  14. src/vm/tool_dispatch.h +84 −0
  15. src/vm/utf8.cpp +8 −8
  16. src/vm/vm.cpp +17 −2
src/include/debug.h+1−0
@@ -24,6 +24,7 @@ namespace disvm · 23 unmodified lines
24 stack,24 stack,
25 channel,25 channel,
26 exception,26 exception,
27 tool,
27 memory, // Noisy component28 memory, // Noisy component
28 addressing, // Noisy component [DEBUG build only]29 addressing, // Noisy component [DEBUG build only]
29 30
src/include/disvm.h+7−3
@@ -71,6 +71,10 @@ namespace disvm · 70 unmodified lines
71 // Load the module at the supplied path into the virtual machine71 // Load the module at the supplied path into the virtual machine
72 std::shared_ptr<const runtime::vm_module_t> load_module(const char *path);72 std::shared_ptr<const runtime::vm_module_t> load_module(const char *path);
73 73
74 // Get a collection of all currently loaded modules.
75 // Note this will only include modules that have been loaded by a call to vm_t::load_module.
76 std::vector<std::shared_ptr<const runtime::vm_module_t>> get_loaded_modules() const;
77
74 // Access the scheduler control for this VM.78 // Access the scheduler control for this VM.
75 runtime::vm_scheduler_control_t &get_scheduler_control() const;79 runtime::vm_scheduler_control_t &get_scheduler_control() const;
76 80
@@ -86,7 +90,7 @@ namespace disvm · 9 unmodified lines
86 class loaded_module_t final90 class loaded_module_t final
87 {91 {
88 public:92 public:
89 loaded_module_t(std::unique_ptr<runtime::vm_string_t> origin, std::shared_ptr<const runtime::vm_module_t> module);93 loaded_module_t(std::unique_ptr<runtime::vm_string_t> origin, std::shared_ptr<runtime::vm_module_t> module);
90 loaded_module_t(const loaded_module_t &) = delete;94 loaded_module_t(const loaded_module_t &) = delete;
91 loaded_module_t &operator=(const loaded_module_t &) = delete;95 loaded_module_t &operator=(const loaded_module_t &) = delete;
92 96
@@ -97,11 +101,11 @@ namespace disvm · 4 unmodified lines
97 std::unique_ptr<runtime::vm_string_t> origin;101 std::unique_ptr<runtime::vm_string_t> origin;
98 102
99 // Weak reference to a loaded module103 // Weak reference to a loaded module
100 std::weak_ptr<const runtime::vm_module_t> module;104 std::weak_ptr<runtime::vm_module_t> module;
101 };105 };
102 106
103 using loaded_modules_t = std::forward_list<loaded_module_t>;107 using loaded_modules_t = std::forward_list<loaded_module_t>;
104 std::mutex _modules_lock;108 mutable std::mutex _modules_lock;
105 loaded_modules_t _modules;109 loaded_modules_t _modules;
106 110
107 std::unique_ptr<runtime::vm_scheduler_t> _scheduler;111 std::unique_ptr<runtime::vm_scheduler_t> _scheduler;
src/include/opcodes.h+4−1
@@ -190,9 +190,12 @@ namespace disvm · 189 unmodified lines
190 expf,190 expf,
191 self,191 self,
192 192
193 // [SPEC] Added opcode for debugging support.
194 brkpt,
195
193 // markers196 // markers
194 first_opcode = invalid,197 first_opcode = invalid,
195 last_opcode = self198 last_opcode = brkpt
196 };199 };
197}200}
198 201
src/include/runtime.h+37−10
@@ -21,7 +21,7 @@ · 20 unmodified lines
21 21
22namespace disvm22namespace disvm
23{23{
24 // VM forward declation24 // Forward declaration
25 class vm_t;25 class vm_t;
26 26
27 namespace runtime27 namespace runtime
@@ -43,7 +43,7 @@ namespace disvm · 15 unmodified lines
43 // VM memory management43 // VM memory management
44 //44 //
45 45
46 // VM forward declation46 // Forward declaration
47 class vm_alloc_t;47 class vm_alloc_t;
48 48
49 // Finalizer callback for allocations.49 // Finalizer callback for allocations.
@@ -75,7 +75,7 @@ namespace disvm · 25 unmodified lines
75 const vm_alloc_instance_finalizer_t finalizer;75 const vm_alloc_instance_finalizer_t finalizer;
76 };76 };
77 77
78 // Access to type descriptors for VM intrinsic types.78 // Access to type descriptors for VM intrinsic types
79 class intrinsic_type_desc final79 class intrinsic_type_desc final
80 {80 {
81 public:81 public:
@@ -262,7 +262,7 @@ namespace disvm · 180 unmodified lines
262 const bool _is_alloc;262 const bool _is_alloc;
263 };263 };
264 264
265 // VM forward declation265 // Forward declaration
266 class vm_channel_t;266 class vm_channel_t;
267 267
268 // VM request mutex268 // VM request mutex
@@ -299,10 +299,12 @@ namespace disvm · 30 unmodified lines
299 public: //static299 public: //static
300 static std::shared_ptr<const type_descriptor_t> type_desc();300 static std::shared_ptr<const type_descriptor_t> type_desc();
301 301
302 using data_transfer_func_t = void(*)(pointer_t, pointer_t, const type_descriptor_t *);
303
302 public:304 public:
303 vm_channel_t(305 vm_channel_t(
304 std::shared_ptr<const type_descriptor_t> td,306 std::shared_ptr<const type_descriptor_t> td,
305 std::function<void(pointer_t, pointer_t, const type_descriptor_t *)> transfer,307 data_transfer_func_t transfer,
306 word_t buffer_len);308 word_t buffer_len);
307 ~vm_channel_t();309 ~vm_channel_t();
308 310
@@ -331,7 +333,7 @@ namespace disvm · 22 unmodified lines
331 private:333 private:
332 std::mutex _data_lock;334 std::mutex _data_lock;
333 std::shared_ptr<const type_descriptor_t> _data_type;335 std::shared_ptr<const type_descriptor_t> _data_type;
334 std::function<void(pointer_t, pointer_t, const type_descriptor_t *)> _data_transfer;336 data_transfer_func_t _data_transfer;
335 337
336 vm_array_t *_data_buffer;338 vm_array_t *_data_buffer;
337 uint32_t _data_buffer_head; // Next element to remove from buffer339 uint32_t _data_buffer_head; // Next element to remove from buffer
@@ -406,7 +408,7 @@ namespace disvm · 68 unmodified lines
406 using src_data_t = inst_data_generic_t;408 using src_data_t = inst_data_generic_t;
407 using dest_data_t = inst_data_generic_t;409 using dest_data_t = inst_data_generic_t;
408 410
409 // VM forward declation411 // Forward declaration
410 class vm_registers_t;412 class vm_registers_t;
411 413
412 // Instruction execution operation414 // Instruction execution operation
@@ -710,6 +712,7 @@ namespace disvm · 297 unmodified lines
710 712
711 // Forward declaration713 // Forward declaration
712 class vm_thread_t;714 class vm_thread_t;
715 class vm_tool_dispatch_t;
713 716
714 // VM registers717 // VM registers
715 class vm_registers_t718 class vm_registers_t
@@ -721,6 +724,7 @@ namespace disvm · 5 unmodified lines
721 ~vm_registers_t();724 ~vm_registers_t();
722 725
723 const vm_thread_t &thread;726 const vm_thread_t &thread;
727 std::atomic<vm_tool_dispatch_t *> tool_dispatch;
724 vm_stack_t stack; // Frame pointer access (FP)728 vm_stack_t stack; // Frame pointer access (FP)
725 vm_pc_t pc; // Program counter (Index into module code section)729 vm_pc_t pc; // Program counter (Index into module code section)
726 vm_alloc_t *mp_base; // Module data base pointer (MP)730 vm_alloc_t *mp_base; // Module data base pointer (MP)
@@ -752,13 +756,17 @@ namespace disvm · 25 unmodified lines
752 uint32_t parent_thread_id);756 uint32_t parent_thread_id);
753 vm_thread_t(757 vm_thread_t(
754 vm_module_ref_t &entry,758 vm_module_ref_t &entry,
759 uint32_t parent_thread_id,
755 const vm_frame_t &initial_frame,760 const vm_frame_t &initial_frame,
756 vm_pc_t start_pc,761 vm_pc_t start_pc);
757 uint32_t parent_thread_id);
758 762
759 ~vm_thread_t();763 ~vm_thread_t();
760 764
761 vm_thread_state_t execute(vm_t &virtual_machine, const uint32_t quanta);765 vm_thread_state_t execute(vm_t &vm, const uint32_t quanta);
766
767 // There is no way to get the current dispatch value.
768 // Setting the dispatch to 'null' detaches all tool related behavior
769 void set_tool_dispatch(vm_tool_dispatch_t *dispatch);
762 770
763 vm_thread_state_t get_state() const;771 vm_thread_state_t get_state() const;
764 772
@@ -834,6 +842,25 @@ namespace disvm · 69 unmodified lines
834 842
835 // Create a garbage collector that does nothing.843 // Create a garbage collector that does nothing.
836 std::unique_ptr<vm_garbage_collector_t> create_no_op_gc(vm_t &);844 std::unique_ptr<vm_garbage_collector_t> create_no_op_gc(vm_t &);
845
846 // Forward declaration
847 class vm_tool_controller_t;
848
849 // VM tool interface
850 // Used to implement tools (e.g. debugger or profiler)
851 class vm_tool_t
852 {
853 public:
854 virtual ~vm_tool_t() = 0;
855
856 // Called when the tool is loaded into the VM
857 virtual void on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t tool_id) = 0;
858
859 // Called when the tool is unloaded from the VM.
860 // Note once the tool has been unloaded the vm and controller references supplied during load
861 // should not be used as they may no longer be valid.
862 virtual void on_unload() = 0;
863 };
837 }864 }
838}865}
839 866
src/include/utf8.h+2−2
@@ -33,7 +33,7 @@ namespace disvm · 32 unmodified lines
33 // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.33 // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
34 std::size_t count_codepoints(const uint8_t *str, const std::size_t max_len);34 std::size_t count_codepoints(const uint8_t *str, const std::size_t max_len);
35 35
36 enum class decode_state36 enum class decode_state_t
37 {37 {
38 accept = 0,38 accept = 0,
39 reject = 1239 reject = 12
@@ -41,7 +41,7 @@ namespace disvm · 1 unmodified line
41 41
42 // Decode a byte at a time to form a codepoint.42 // Decode a byte at a time to form a codepoint.
43 // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.43 // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
44 decode_state decode_step(decode_state &state, rune_t &codepoint, uint8_t byte);44 decode_state_t decode_step(decode_state_t &state, rune_t &codepoint, uint8_t byte);
45 45
46 // Decode the next codepoint in the supplied string.46 // Decode the next codepoint in the supplied string.
47 // Returns number of characters in string consumed.47 // Returns number of characters in string consumed.
src/include/vm_tools.h+63−0
@@ -0,0 +1,63 @@
1//
2// Dis VM
3// File: vm_tools.h
4// Author: arr
5//
6
7#ifndef _DISVM_SRC_INCLUDE_VM_TOOLS_H_
8#define _DISVM_SRC_INCLUDE_VM_TOOLS_H_
9
10#include <cstdint>
11#include <vector>
12#include <memory>
13#include <functional>
14#include "runtime.h"
15
16namespace disvm
17{
18 namespace runtime
19 {
20 union vm_context_value_t
21 {
22 std::size_t i;
23 vm_t *vm;
24 vm_module_t *module;
25 vm_registers_t *registers;
26 vm_alloc_t *alloc;
27 };
28
29 struct vm_event_context_t
30 {
31 vm_context_value_t value1;
32 vm_context_value_t value2;
33 };
34
35 enum class vm_event_t
36 {
37 module_load, // value1: vm_module_t
38 module_unload, // value1: vm_module_t
39 thread_begin, // value1: vm_registers_t
40 thread_end, // value1: vm_registers_t
41 exception_raised, // value1: vm_registers_t value2: vm_alloc_t
42 exception_unhandled, // value1: vm_registers_t value2: vm_alloc_t
43 breakpoint, // value1: vm_registers_t value2: std::size_t
44 };
45
46 using vm_event_callback_t = std::function<void(vm_event_t, vm_event_context_t &)>;
47
48 // VM tool controller
49 class vm_tool_controller_t
50 {
51 public:
52 // Call the supplied callback when the specified event occurs
53 virtual std::size_t subscribe_event(vm_event_t, vm_event_callback_t) = 0;
54 virtual void unsubscribe_event(std::size_t) = 0;
55
56 // Set a breakpoint in the supplied module at the specified PC location
57 virtual std::size_t set_breakpoint(std::shared_ptr<const vm_module_t>, vm_pc_t) = 0;
58 virtual void clear_breakpoint(std::size_t) = 0;
59 };
60 }
61}
62
63#endif // _DISVM_SRC_INCLUDE_VM_TOOLS_H_
src/vm/channel.cpp+1−1
@@ -29,7 +29,7 @@ std::shared_ptr<const type_descriptor_t> vm_channel_t::type_desc() · 28 unmodified lines
29 29
30vm_channel_t::vm_channel_t(30vm_channel_t::vm_channel_t(
31 std::shared_ptr<const type_descriptor_t> td,31 std::shared_ptr<const type_descriptor_t> td,
32 std::function<void(pointer_t, pointer_t, const type_descriptor_t *)> transfer,32 data_transfer_func_t transfer,
33 word_t buffer_len)33 word_t buffer_len)
34 : vm_alloc_t(vm_channel_t::type_desc())34 : vm_alloc_t(vm_channel_t::type_desc())
35 , _data_buffer{ nullptr }35 , _data_buffer{ nullptr }
src/vm/disvm.gyp+1−0
@@ -17,6 +17,7 @@ · 16 unmodified lines
17 'stack.cpp',17 'stack.cpp',
18 'string.cpp',18 'string.cpp',
19 'thread.cpp',19 'thread.cpp',
20 'tool_dispatch.cpp',
20 'utf8.cpp',21 'utf8.cpp',
21 'vm.cpp',22 'vm.cpp',
22 'vm_memory.cpp',23 'vm_memory.cpp',
src/vm/execution_table.cpp+26−5
@@ -18,6 +18,7 @@ · 17 unmodified lines
18#include <exceptions.h>18#include <exceptions.h>
19#include <debug.h>19#include <debug.h>
20#include <builtin_module.h>20#include <builtin_module.h>
21#include "tool_dispatch.h"
21#include "execution_table.h"22#include "execution_table.h"
22 23
23using namespace disvm;24using namespace disvm;
@@ -186,7 +187,7 @@ namespace · 162 unmodified lines
186 auto buffer = std::array<char, sizeof("�2147483648")>{};187 auto buffer = std::array<char, sizeof("�2147483648")>{};
187 188
188 const auto w = vt_ref<word_t>(r.src);189 const auto w = vt_ref<word_t>(r.src);
189 const auto len = ::sprintf(buffer.data(), "%d", w);190 const auto len = std::sprintf(buffer.data(), "%d", w);
190 assert(static_cast<std::size_t>(len) <= buffer.size());191 assert(static_cast<std::size_t>(len) <= buffer.size());
191 192
192 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));193 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));
@@ -212,7 +213,7 @@ namespace · 19 unmodified lines
212 auto buffer = std::array<char, sizeof("-2.2250738585072014e-308")>{};213 auto buffer = std::array<char, sizeof("-2.2250738585072014e-308")>{};
213 214
214 const auto real = vt_ref<real_t>(r.src);215 const auto real = vt_ref<real_t>(r.src);
215 const auto len = ::sprintf(buffer.data(), "%g", real);216 const auto len = std::sprintf(buffer.data(), "%g", real);
216 assert(static_cast<std::size_t>(len) <= buffer.size());217 assert(static_cast<std::size_t>(len) <= buffer.size());
217 218
218 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));219 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));
@@ -238,7 +239,7 @@ namespace · 19 unmodified lines
238 auto buffer = std::array<char, sizeof("-9223372036854775808")>{};239 auto buffer = std::array<char, sizeof("-9223372036854775808")>{};
239 240
240 const auto b = vt_ref<big_t>(r.src);241 const auto b = vt_ref<big_t>(r.src);
241 const auto len = ::sprintf(buffer.data(), "%lld", b);242 const auto len = std::sprintf(buffer.data(), "%lld", b);
242 assert(static_cast<std::size_t>(len) <= buffer.size());243 assert(static_cast<std::size_t>(len) <= buffer.size());
243 244
244 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));245 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));
@@ -643,6 +644,7 @@ namespace · 398 unmodified lines
643 break;644 break;
644 }645 }
645 646
647 assert(0 <= static_cast<std::size_t>(target_pc) && static_cast<std::size_t>(target_pc) < r.module_ref->code_section.size());
646 r.pc = target_pc;648 r.pc = target_pc;
647 }649 }
648 650
@@ -1036,7 +1038,7 @@ namespace · 387 unmodified lines
1036 vm_registers_t &r,1038 vm_registers_t &r,
1037 const vm_t &vm,1039 const vm_t &vm,
1038 std::shared_ptr<const type_descriptor_t> type_desc,1040 std::shared_ptr<const type_descriptor_t> type_desc,
1039 std::function<void(pointer_t, pointer_t, const type_descriptor_t *)> transfer)1041 vm_channel_t::data_transfer_func_t transfer)
1040 {1042 {
1041 auto buffer_len = word_t{ 0 };1043 auto buffer_len = word_t{ 0 };
1042 if (r.mid != r.dest)1044 if (r.mid != r.dest)
@@ -1066,7 +1068,7 @@ namespace · 23 unmodified lines
1066 {1068 {
1067 auto memory_size = vt_ref<word_t>(r.src); 1069 auto memory_size = vt_ref<word_t>(r.src);
1068 auto channel_data_type = type_descriptor_t::create(memory_size); 1070 auto channel_data_type = type_descriptor_t::create(memory_size);
1069 1071
1070 _newc_(r, vm, channel_data_type, _channel_movm);1072 _newc_(r, vm, channel_data_type, _channel_movm);
1071 }1073 }
1072 1074
@@ -1669,6 +1671,24 @@ namespace · 596 unmodified lines
1669 r.pc = handler_pc;1671 r.pc = handler_pc;
1670 }1672 }
1671 1673
1674 // [SPEC] Added to support debugger.
1675 // The actual opcode for execution can be retrieved from the tool dispatch interface.
1676 // The current register state is for the real opcode, the breakpoint opcode has no inputs/outputs.
1677 EXEC_DECL(brkpt)
1678 {
1679 auto tool_dispatch = r.tool_dispatch.load();
1680 if (tool_dispatch == nullptr)
1681 {
1682 assert(false && "Breakpoint set but no tool dispatcher found" );
1683 throw vm_system_exception{ "Breakpoint set but no tool dispatcher found" };
1684 }
1685
1686 const auto original_op = tool_dispatch->on_breakpoint(r, vm);
1687
1688 // Continue execution
1689 vm_exec_table[static_cast<std::size_t>(original_op)](r, vm);
1690 }
1691
1672 //1692 //
1673 // Module operations1693 // Module operations
1674 //1694 //
@@ -1895,4 +1915,5 @@ const vm_exec_t disvm::runtime::vm_exec_table[static_cast<std::size_t>(opcode_t: · 220 unmodified lines
1895 expl,1915 expl,
1896 expf,1916 expf,
1897 notimpl, // self,1917 notimpl, // self,
1918 brkpt,
1898};1919};
src/vm/scheduler.cpp+10−3
@@ -5,6 +5,7 @@ · 4 unmodified lines
5//5//
6 6
7#include <debug.h>7#include <debug.h>
8#include <iostream>
8#include <sstream>9#include <sstream>
9#include <exceptions.h>10#include <exceptions.h>
10#include "scheduler.h"11#include "scheduler.h"
@@ -52,8 +53,7 @@ void default_scheduler_t::worker_main(default_scheduler_t &instance) · 41 unmodified lines
52 if (current_thread != nullptr)53 if (current_thread != nullptr)
53 {54 {
54 const auto &r = current_thread->vm_thread->get_registers();55 const auto &r = current_thread->vm_thread->get_registers();
55 walk_stack(r, [&err_msg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref)56 walk_stack(r, [&err_msg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref) {
56 {
57 auto module_name = "<No Name>";57 auto module_name = "<No Name>";
58 if (module_ref.module->module_name != nullptr)58 if (module_ref.module->module_name != nullptr)
59 module_name = module_ref.module->module_name->str();59 module_name = module_ref.module->module_name->str();
@@ -63,7 +63,9 @@ void default_scheduler_t::worker_main(default_scheduler_t &instance) · 3 unmodified lines
63 });63 });
64 }64 }
65 65
66 std::fprintf(stderr, "%s\n", err_msg.str().c_str());66 auto err_str = err_msg.str();
67 std::cerr << err_str.c_str() << std::endl;
68
67 instance._terminating = true;69 instance._terminating = true;
68 instance._worker_event.notify_all();70 instance._worker_event.notify_all();
69 }71 }
@@ -78,6 +80,11 @@ default_scheduler_t::default_scheduler_t(vm_t &vm, uint32_t system_thread_count, · 8 unmodified lines
78 , _vm{ vm }80 , _vm{ vm }
79 , _vm_thread_quanta{ thread_quanta }81 , _vm_thread_quanta{ thread_quanta }
80{82{
83 if (_worker_thread_count == 0)
84 throw vm_system_exception{ "Work thread count must be > 0" };
85
86 if (_vm_thread_quanta == 0)
87 throw vm_system_exception{ "Work thread quanta must be > 0" };
81}88}
82 89
83default_scheduler_t::~default_scheduler_t()90default_scheduler_t::~default_scheduler_t()
src/vm/sys/Sysmod.cpp+3−3
@@ -292,21 +292,21 @@ Sys_byte2char(vm_registers_t &r, vm_t &vm) · 291 unmodified lines
292 fp.ret->t1 = 0;292 fp.ret->t1 = 0;
293 fp.ret->t2 = 0;293 fp.ret->t2 = 0;
294 294
295 auto state = utf8::decode_state::accept;295 auto state = utf8::decode_state_t::accept;
296 auto result = rune_t{};296 auto result = rune_t{};
297 const auto buffer_len = buffer->get_length();297 const auto buffer_len = buffer->get_length();
298 for (auto i = n; i < buffer_len; ++i)298 for (auto i = n; i < buffer_len; ++i)
299 {299 {
300 auto b = buffer->at<byte_t>(i);300 auto b = buffer->at<byte_t>(i);
301 state = utf8::decode_step(state, result, b);301 state = utf8::decode_step(state, result, b);
302 if (state == utf8::decode_state::accept)302 if (state == utf8::decode_state_t::accept)
303 {303 {
304 fp.ret->t0 = result;304 fp.ret->t0 = result;
305 fp.ret->t1 = (i - n) + 1; // Number of bytes consumed305 fp.ret->t1 = (i - n) + 1; // Number of bytes consumed
306 fp.ret->t2 = 1;306 fp.ret->t2 = 1;
307 return;307 return;
308 }308 }
309 else if (state == utf8::decode_state::reject)309 else if (state == utf8::decode_state_t::reject)
310 {310 {
311 fp.ret->t1 = 1;311 fp.ret->t1 = 1;
312 return;312 return;
src/vm/thread.cpp+18−7
@@ -133,6 +133,7 @@ vm_registers_t::vm_registers_t( · 132 unmodified lines
133 , thread{ thread }133 , thread{ thread }
134 , pc{ entry.module->header.entry_pc }134 , pc{ entry.module->header.entry_pc }
135 , stack{ entry.module->header.stack_extent }135 , stack{ entry.module->header.stack_extent }
136 , tool_dispatch{ nullptr }
136 , src{ nullptr }137 , src{ nullptr }
137 , mid{ nullptr }138 , mid{ nullptr }
138 , dest{ nullptr }139 , dest{ nullptr }
@@ -203,9 +204,9 @@ vm_thread_t::vm_thread_t( · 64 unmodified lines
203 204
204vm_thread_t::vm_thread_t(205vm_thread_t::vm_thread_t(
205 vm_module_ref_t &entry,206 vm_module_ref_t &entry,
207 uint32_t parent_thread_id,
206 const vm_frame_t &initial_frame,208 const vm_frame_t &initial_frame,
207 vm_pc_t start_pc,209 vm_pc_t start_pc)
208 uint32_t parent_thread_id)
209 : vm_alloc_t(vm_thread_t::type_desc())210 : vm_alloc_t(vm_thread_t::type_desc())
210 , _parent_thread_id{ parent_thread_id }211 , _parent_thread_id{ parent_thread_id }
211 , _registers{ *this, entry }212 , _registers{ *this, entry }
@@ -236,7 +237,7 @@ vm_thread_t::~vm_thread_t() · 24 unmodified lines
236 debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "destroy: vm thread: %d %d\n", _thread_id, _parent_thread_id);237 debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "destroy: vm thread: %d %d\n", _thread_id, _parent_thread_id);
237}238}
238 239
239vm_thread_state_t vm_thread_t::execute(vm_t &virtual_machine, const uint32_t quanta)240vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta)
240{241{
241 _registers.current_thread_state = vm_thread_state_t::running;242 _registers.current_thread_state = vm_thread_state_t::running;
242 assert(_registers.pc != runtime_constants::invalid_program_counter);243 assert(_registers.pc != runtime_constants::invalid_program_counter);
@@ -249,7 +250,9 @@ vm_thread_state_t vm_thread_t::execute(vm_t &virtual_machine, const uint32_t qua · 6 unmodified lines
249 {250 {
250 if (_registers.stack.peek_frame() == nullptr)251 if (_registers.stack.peek_frame() == nullptr)
251 {252 {
252 debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "exit: vm thread: stack exhausted: %d\n", _thread_id);253 if (debug::is_component_tracing_enabled<debug::component_trace_t::thread>())
254 debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "exit: vm thread: stack exhausted: %d\n", _thread_id);
255
253 _registers.current_thread_state = vm_thread_state_t::empty_stack;256 _registers.current_thread_state = vm_thread_state_t::empty_stack;
254 break;257 break;
255 }258 }
@@ -257,14 +260,16 @@ vm_thread_state_t vm_thread_t::execute(vm_t &virtual_machine, const uint32_t qua · 1 unmodified line
257 const auto &code_section = _registers.module_ref->code_section;260 const auto &code_section = _registers.module_ref->code_section;
258 assert(static_cast<std::size_t>(_registers.pc) < code_section.size());261 assert(static_cast<std::size_t>(_registers.pc) < code_section.size());
259 262
263 assert(!_registers.module_ref->is_builtin_module() && "Interpreter thread is unable to execute native instructions");
260 const auto &inst = code_section[_registers.pc].op;264 const auto &inst = code_section[_registers.pc].op;
261 const auto op_code = static_cast<std::size_t>(inst.opcode);265 const auto opcode = static_cast<std::size_t>(inst.opcode);
262 if (static_cast<std::size_t>(opcode_t::last_opcode) < op_code)266 if (static_cast<std::size_t>(opcode_t::last_opcode) < opcode)
263 throw vm_system_exception{ "Unknown op-code" };267 throw vm_system_exception{ "Unknown op-code" };
264 268
269 // Instruction is valid, decode and update registers for operation
265 decode_address(inst, _registers);270 decode_address(inst, _registers);
266 _registers.pc++;271 _registers.pc++;
267 vm_exec_table[op_code](_registers, virtual_machine);272 vm_exec_table[opcode](_registers, vm);
268 273
269 if (_registers.current_thread_state != vm_thread_state_t::running)274 if (_registers.current_thread_state != vm_thread_state_t::running)
270 break;275 break;
@@ -307,6 +312,12 @@ vm_thread_state_t vm_thread_t::execute(vm_t &virtual_machine, const uint32_t qua · 36 unmodified lines
307 return _registers.current_thread_state;312 return _registers.current_thread_state;
308}313}
309 314
315void vm_thread_t::set_tool_dispatch(vm_tool_dispatch_t *dispatch)
316{
317 assert((_registers.tool_dispatch.load() == nullptr) != (dispatch == nullptr) && "Dispatch value should toggle between null and non-null");
318 _registers.tool_dispatch = dispatch;
319}
320
310vm_thread_state_t vm_thread_t::get_state() const321vm_thread_state_t vm_thread_t::get_state() const
311{322{
312 return _registers.current_thread_state;323 return _registers.current_thread_state;
src/vm/tool_dispatch.cpp+232−0
@@ -0,0 +1,232 @@
1//
2// Dis VM
3// File: tool_dispatch.cpp
4// Author: arr
5//
6
7#include <cassert>
8#include <debug.h>
9#include <vm_tools.h>
10#include <exceptions.h>
11#include <unordered_set>
12#include <utils.h>
13#include "tool_dispatch.h"
14
15using namespace disvm;
16using namespace disvm::runtime;
17
18// Empty destructor for vm tool 'interface'
19vm_tool_t::~vm_tool_t()
20{
21}
22
23vm_tool_dispatch_t::vm_tool_dispatch_t(vm_t &vm)
24 : _vm{ vm }
25{
26 _events.cookie_counter = 1;
27 _breakpoints.cookie_counter = 1;
28}
29
30vm_tool_dispatch_t::~vm_tool_dispatch_t()
31{
32 std::lock_guard<std::mutex> lock{ _tools_lock };
33 for (auto &t : _tools)
34 t.second->on_unload();
35}
36
37std::size_t vm_tool_dispatch_t::load_tool(std::shared_ptr<runtime::vm_tool_t> tool)
38{
39 static std::size_t _tool_id_counter{ 1 };
40
41 if (tool == nullptr)
42 throw vm_system_exception{ "Tool cannot be null" };
43
44 // Keep the lock for the collection until the tool has been completed being loaded.
45 std::lock_guard<std::mutex> lock{ _tools_lock };
46 const auto current_id = _tool_id_counter++;
47 _tools[current_id] = tool;
48
49 try
50 {
51 // [TODO] For debug builds, it might be useful to wrap the controller in a proxy that records
52 // the subscribed callbacks and asserts they are all properly unsubscribed during agent unload.
53 tool->on_load(_vm, *this, current_id);
54 }
55 catch (...)
56 {
57 if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
58 debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::warning, "load: tool: failure");
59
60 // The ID is tainted and will not be re-used
61 _tools.erase(current_id);
62 throw;
63 }
64
65 if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
66 debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "load: tool: %d", current_id);
67
68 return current_id;
69}
70
71std::size_t vm_tool_dispatch_t::unload_tool(std::size_t tool_id)
72{
73 // Keep the lock for the collection until the tool has been completed being unloaded.
74 std::lock_guard<std::mutex> lock{ _tools_lock };
75 auto iter = _tools.find(tool_id);
76 if (iter == _tools.cend())
77 return _tools.size();
78
79 auto tool = iter->second;
80 _tools.erase(iter);
81
82 if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
83 debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "unload: tool: %d", tool_id);
84
85 assert(tool != nullptr);
86 tool->on_unload();
87
88 return _tools.size();
89}
90
91opcode_t vm_tool_dispatch_t::on_breakpoint(vm_registers_t &r, vm_t &vm)
92{
93 return get_original_opcode(r.module_ref->module.get(), r.pc - 1);
94}
95
96std::size_t vm_tool_dispatch_t::subscribe_event(vm_event_t evt, vm_event_callback_t cb)
97{
98 if (!cb)
99 throw vm_system_exception{ "Invalid callback for event" };
100
101 std::lock_guard<std::mutex> lock{ _events.lock };
102
103 const auto cookie_id = _events.cookie_counter++;
104
105 // Store the event callback
106 auto &event_callbacks = _events.callbacks[evt];
107 event_callbacks[cookie_id] = cb;
108
109 // Store the cookie with the associate event
110 _events.cookie_to_event[cookie_id] = evt;
111
112 if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
113 debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "subscribe: event: %d %d", evt, cookie_id);
114
115 return cookie_id;
116}
117
118void vm_tool_dispatch_t::unsubscribe_event(std::size_t cookie_id)
119{
120 std::lock_guard<std::mutex> lock{ _events.lock };
121
122 auto iter = _events.cookie_to_event.find(cookie_id);
123 if (iter == _events.cookie_to_event.cend())
124 return;
125
126 // Erase cookie and event
127 const auto evt = iter->second;
128 auto &event_callbacks = _events.callbacks[evt];
129 event_callbacks.erase(cookie_id);
130 _events.cookie_to_event.erase(iter);
131
132 if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
133 debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "unsubscribe: event: %d %d", evt, cookie_id);
134}
135
136std::size_t vm_tool_dispatch_t::set_breakpoint(std::shared_ptr<const vm_module_t> module, vm_pc_t pc)
137{
138 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" };
140
141 // [TODO] This should be re-considered. Removing 'const' is less than ideal.
142 // Remove the const value since the code section needs to be updated.
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" };
146
147 const auto real_opcode = code_section[pc].op.opcode;
148
149 {
150 std::lock_guard<std::mutex> lock{ _breakpoints.lock };
151 if (real_opcode == opcode_t::brkpt)
152 throw vm_system_exception{ "Breakpoint already set at PC" };
153
154 const auto cookie_id = _breakpoints.cookie_counter++;
155
156 // Map the cookie to the module/pc pair.
157 _breakpoints.cookie_to_modulepc[cookie_id] = std::make_pair(module, pc);
158
159 // Record the original opcode
160 auto &pc_map = _breakpoints.original_opcodes[reinterpret_cast<std::uintptr_t>(module.get())];
161 pc_map[pc] = real_opcode;
162
163 // Replace the current opcode with breakpoint
164 code_section[pc].op.opcode = opcode_t::brkpt;
165
166 if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
167 debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "breakpoint: set: %d %d >>%s<<", cookie_id, pc, module->module_name->str());
168
169 return cookie_id;
170 }
171}
172
173void vm_tool_dispatch_t::clear_breakpoint(std::size_t cookie_id)
174{
175 std::lock_guard<std::mutex> lock{ _breakpoints.lock };
176
177 // Resolve the cookie to the module/pc pair
178 auto iter_cookie = _breakpoints.cookie_to_modulepc.find(cookie_id);
179 if (iter_cookie == _breakpoints.cookie_to_modulepc.cend())
180 return;
181
182 auto mod_pc_pair = iter_cookie->second;
183 const auto target_pc = mod_pc_pair.second;
184
185 // Determine the original opcode
186 auto iter_orig = _breakpoints.original_opcodes.find(reinterpret_cast<std::uintptr_t>(mod_pc_pair.first.get()));
187 assert(iter_orig != _breakpoints.original_opcodes.cend());
188
189 auto &pc_map = iter_orig->second;
190 auto iter_pc = pc_map.find(target_pc);
191 assert(iter_pc != pc_map.cend());
192
193 auto original_opcode = iter_pc->second;
194
195 // Replace the breakpoint opcode with the original
196 auto &code_section = const_cast<code_section_t &>(mod_pc_pair.first->code_section);
197 assert(code_section[target_pc].op.opcode == opcode_t::brkpt);
198 code_section[target_pc].op.opcode = original_opcode;
199
200 if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
201 debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "breakpoint: unset: %d %d >>%s<<", cookie_id, target_pc, mod_pc_pair.first->module_name->str());
202
203 // Clean-up the PC mapping to opcode
204 pc_map.erase(iter_pc);
205
206 // If the PC map is empty, erase the module mapping as well
207 if (pc_map.size() == 0)
208 _breakpoints.original_opcodes.erase(iter_orig);
209
210 // Remove the cookie map
211 _breakpoints.cookie_to_modulepc.erase(iter_cookie);
212}
213
214opcode_t vm_tool_dispatch_t::get_original_opcode(const vm_module_t *module, vm_pc_t pc)
215{
216 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" };
218
219 std::lock_guard<std::mutex> lock{ _breakpoints.lock };
220
221 auto iter_orig = _breakpoints.original_opcodes.find(reinterpret_cast<std::uintptr_t>(module));
222 if (iter_orig != _breakpoints.original_opcodes.cend())
223 {
224 auto &pc_map = iter_orig->second;
225 auto iter_pc = pc_map.find(pc);
226 if (iter_pc != pc_map.cend())
227 return iter_pc->second;
228 }
229
230 assert(false && "Why are we missing this information?");
231 return opcode_t::runt; // Return NOP instruction?
232}
src/vm/tool_dispatch.h+84−0
@@ -0,0 +1,84 @@
1//
2// Dis VM
3// File: tool_dispatch.h
4// Author: arr
5//
6
7#ifndef _DISVM_SRC_VM_TOOL_DISPATCH_H_
8#define _DISVM_SRC_VM_TOOL_DISPATCH_H_
9
10#include <cstdint>
11#include <memory>
12#include <unordered_map>
13#include <mutex>
14#include <runtime.h>
15#include <vm_tools.h>
16
17namespace disvm
18{
19 namespace runtime
20 {
21 class vm_tool_dispatch_t final : private vm_tool_controller_t
22 {
23 public:
24 vm_tool_dispatch_t(vm_t &vm);
25
26 virtual ~vm_tool_dispatch_t();
27
28 // Loads a tool for dispatch and returns the tool's ID
29 std::size_t load_tool(std::shared_ptr<runtime::vm_tool_t> tool);
30
31 // Unloads the tool associated with the supplied ID and returns the current tool count
32 std::size_t unload_tool(std::size_t tool_id);
33
34 // Handle a breakpoint event.
35 // Returns opcode that was replaced by breakpoint opcode.
36 opcode_t on_breakpoint(vm_registers_t &r, vm_t &vm);
37
38 public: // vm_tool_controller_t
39 std::size_t subscribe_event(vm_event_t evt, vm_event_callback_t cb) override final;
40 void unsubscribe_event(std::size_t cookie_id) override final;
41
42 std::size_t set_breakpoint(std::shared_ptr<const vm_module_t> module, vm_pc_t pc) override final;
43 void clear_breakpoint(std::size_t cookie_id) override final;
44
45 private:
46 opcode_t get_original_opcode(const vm_module_t *module, vm_pc_t pc);
47
48 private:
49 vm_t &_vm;
50
51 using tool_collection_t = std::unordered_map<std::size_t, std::shared_ptr<vm_tool_t>>;
52 tool_collection_t _tools;
53 std::mutex _tools_lock;
54
55 struct
56 {
57 using cookie_to_callback_map_t = std::unordered_map<std::size_t, vm_event_callback_t>;
58 using event_to_callbacks_map_t = std::unordered_map<vm_event_t, cookie_to_callback_map_t>;
59 event_to_callbacks_map_t callbacks;
60
61 using cookie_to_event_map_t = std::unordered_map<std::size_t, vm_event_t>;
62 cookie_to_event_map_t cookie_to_event;
63
64 std::mutex lock;
65 std::size_t cookie_counter;
66 } _events;
67
68 struct
69 {
70 using cookie_to_modulepc_t = std::unordered_map<vm_pc_t, std::pair<std::shared_ptr<const vm_module_t>, vm_pc_t>>;
71 cookie_to_modulepc_t cookie_to_modulepc;
72
73 using pc_to_opcode_map_t = std::unordered_map<vm_pc_t, opcode_t>;
74 using module_to_pc_map_t = std::unordered_map<std::uintptr_t, pc_to_opcode_map_t>;
75 module_to_pc_map_t original_opcodes;
76
77 std::mutex lock;
78 std::size_t cookie_counter;
79 } _breakpoints;
80 };
81 }
82}
83
84#endif // _DISVM_SRC_VM_TOOL_DISPATCH_H_
src/vm/utf8.cpp+8−8
@@ -40,15 +40,15 @@ namespace · 39 unmodified lines
40}40}
41 41
42// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.42// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
43utf8::decode_state disvm::runtime::utf8::decode_step(utf8::decode_state &state, rune_t &codepoint, uint8_t byte)43utf8::decode_state_t disvm::runtime::utf8::decode_step(utf8::decode_state_t &state, rune_t &codepoint, uint8_t byte)
44{44{
45 auto type = uint32_t{ utf8d[byte] };45 auto type = uint32_t{ utf8d[byte] };
46 46
47 codepoint = (state != utf8::decode_state::accept) ?47 codepoint = (state != utf8::decode_state_t::accept) ?
48 (byte & 0x3fu) | (codepoint << 6) :48 (byte & 0x3fu) | (codepoint << 6) :
49 (0xff >> type) & (byte);49 (0xff >> type) & (byte);
50 50
51 state = static_cast<utf8::decode_state>(utf8d[256 + static_cast<uint32_t>(state) + type]);51 state = static_cast<utf8::decode_state_t>(utf8d[256 + static_cast<uint32_t>(state) + type]);
52 return state;52 return state;
53}53}
54 54
@@ -59,15 +59,15 @@ std::size_t disvm::runtime::utf8::count_codepoints(const uint8_t *str, const std · 4 unmodified lines
59 assert(str != nullptr);59 assert(str != nullptr);
60 auto single_character = rune_t{};60 auto single_character = rune_t{};
61 auto points = std::size_t{ 0 };61 auto points = std::size_t{ 0 };
62 auto state = utf8::decode_state::accept;62 auto state = utf8::decode_state_t::accept;
63 63
64 for (auto i = std::size_t{ 0 }; i < max_len; ++i)64 for (auto i = std::size_t{ 0 }; i < max_len; ++i)
65 {65 {
66 if (utf8::decode_state::accept == utf8::decode_step(state, single_character, str[i]))66 if (utf8::decode_state_t::accept == utf8::decode_step(state, single_character, str[i]))
67 points++;67 points++;
68 }68 }
69 69
70 if (state != utf8::decode_state::accept)70 if (state != utf8::decode_state_t::accept)
71 throw invalid_utf8{};71 throw invalid_utf8{};
72 72
73 return points;73 return points;
@@ -76,7 +76,7 @@ std::size_t disvm::runtime::utf8::count_codepoints(const uint8_t *str, const std · 2 unmodified lines
76std::size_t disvm::runtime::utf8::decode(const uint8_t *str, rune_t &codepoint)76std::size_t disvm::runtime::utf8::decode(const uint8_t *str, rune_t &codepoint)
77{77{
78 assert(str != nullptr);78 assert(str != nullptr);
79 auto state = utf8::decode_state::accept;79 auto state = utf8::decode_state_t::accept;
80 80
81 codepoint = 0;81 codepoint = 0;
82 auto begin = str;82 auto begin = str;
@@ -86,7 +86,7 @@ std::size_t disvm::runtime::utf8::decode(const uint8_t *str, rune_t &codepoint) · 3 unmodified lines
86 {86 {
87 curr++;87 curr++;
88 state = utf8::decode_step(state, codepoint, c);88 state = utf8::decode_step(state, codepoint, c);
89 if (state == utf8::decode_state::accept)89 if (state == utf8::decode_state_t::accept)
90 return (curr - begin);90 return (curr - begin);
91 }91 }
92 92
src/vm/vm.cpp+17−2
@@ -19,7 +19,7 @@ · 18 unmodified lines
19using namespace disvm;19using namespace disvm;
20using namespace disvm::runtime;20using namespace disvm::runtime;
21 21
22vm_t::loaded_module_t::loaded_module_t(std::unique_ptr<runtime::vm_string_t> origin, std::shared_ptr<const runtime::vm_module_t> module)22vm_t::loaded_module_t::loaded_module_t(std::unique_ptr<runtime::vm_string_t> origin, std::shared_ptr<runtime::vm_module_t> module)
23 : origin{ std::move(origin) }23 : origin{ std::move(origin) }
24 , module{ module }24 , module{ module }
25{25{
@@ -126,7 +126,7 @@ const runtime::vm_thread_t& vm_t::fork( · 100 unmodified lines
126 if (initial_pc < 0 || static_cast<vm_pc_t>(module_ref.code_section.size()) <= initial_pc)126 if (initial_pc < 0 || static_cast<vm_pc_t>(module_ref.code_section.size()) <= initial_pc)
127 throw vm_system_exception{ "Invalid entry program counter" };127 throw vm_system_exception{ "Invalid entry program counter" };
128 128
129 auto thread = std::make_unique<vm_thread_t>(module_ref, initial_frame, initial_pc, parent.get_thread_id());129 auto thread = std::make_unique<vm_thread_t>(module_ref, parent.get_thread_id(), initial_frame, initial_pc);
130 return _scheduler->schedule_thread(std::move(thread));130 return _scheduler->schedule_thread(std::move(thread));
131}131}
132 132
@@ -179,6 +179,21 @@ std::shared_ptr<const runtime::vm_module_t> vm_t::load_module(const char *path) · 46 unmodified lines
179 return new_module;179 return new_module;
180}180}
181 181
182std::vector<std::shared_ptr<const runtime::vm_module_t>> vm_t::get_loaded_modules() const
183{
184 auto result = std::vector<std::shared_ptr<const runtime::vm_module_t>>{};
185
186 std::lock_guard<std::mutex> lock{ _modules_lock };
187 for (auto &m : _modules)
188 {
189 auto loaded_module_maybe = m.module.lock();
190 if (loaded_module_maybe != nullptr)
191 result.push_back(loaded_module_maybe);
192 }
193
194 return result;
195}
196
182runtime::vm_scheduler_control_t &vm_t::get_scheduler_control() const197runtime::vm_scheduler_control_t &vm_t::get_scheduler_control() const
183{198{
184 return _scheduler->get_controller();199 return _scheduler->get_controller();