History

Create configuration struct for VM Add syscall failure exception - Create VM syscall failure message functions Respect the 'r' format verb Create module resolver interface Implement default module resolver interface Implement Sys.fildes() function

arr committed 9 years ago

Commit 232a6591 parent eb06d3d17 files · +416 −124Browse files

Unified · Split

17 files
  1. src/exec/main.cpp +14 −22
  2. src/include/disvm.h +35 −12
  3. src/include/exceptions.h +14 −4
  4. src/include/runtime.h +13 −0
  5. src/include/utils.h +23 −17
  6. src/vm/disvm.gyp +1 −0
  7. src/vm/execution_table.cpp +0 −1
  8. src/vm/module_reader.cpp +1 −15
  9. src/vm/module_resolver.cpp +72 −0
  10. src/vm/module_resolver.h +37 −0
  11. src/vm/scheduler.cpp +0 −2
  12. src/vm/sys/Sysmod.cpp +35 −17
  13. src/vm/sys/fd_manager.cpp +1 −1
  14. src/vm/sys/print.cpp +12 −4
  15. src/vm/sys/sys_utils.h +3 −1
  16. src/vm/vm.cpp +146 −26
  17. src/vm/vm_memory.cpp +9 −2
src/exec/main.cpp+14−22
@@ -195,14 +195,12 @@ struct exec_options · 194 unmodified lines
195 , enabled_debugger{ false }195 , enabled_debugger{ false }
196 , quiet_start{ false }196 , quiet_start{ false }
197 , print_help{ false }197 , print_help{ false }
198 , sys_thread_count{ vm_t::default_system_thread_count }198 , vm_config{}
199 , thread_quanta{ vm_t::default_thread_quanta }
200 { }199 { }
201 200
202 std::vector<char *> vm_args;201 std::vector<char *> vm_args;
203 202
204 uint32_t sys_thread_count;203 vm_config_t vm_config;
205 uint32_t thread_quanta;
206 204
207 bool print_help;205 bool print_help;
208 bool quiet_start;206 bool quiet_start;
@@ -220,7 +218,7 @@ void print_banner(const exec_options &options) · 11 unmodified lines
220 std::cout218 std::cout
221 << "\n----------------\n"219 << "\n----------------\n"
222 << "Debugger enabled: " << std::boolalpha << options.enabled_debugger << "\n"220 << "Debugger enabled: " << std::boolalpha << options.enabled_debugger << "\n"
223 << "System thread usage: " << options.sys_thread_count << "\n";221 << "System thread usage: " << options.vm_config.sys_thread_pool_size << "\n";
224 222
225 if (options.enabled_debugger)223 if (options.enabled_debugger)
226 std::cout << "\n" << options.debugger << "\n";224 std::cout << "\n" << options.debugger << "\n";
@@ -274,14 +272,6 @@ void log_callback(const debug::component_trace_t origin, const debug::log_level_ · 47 unmodified lines
274 std::cout << ss.str();272 std::cout << ss.str();
275}273}
276 274
277void enable_logging(debug::component_trace_t c)
278{
279 debug::set_component_tracing(c, true);
280
281 // Constantly setting the callback
282 debug::set_logging_callback(log_callback);
283}
284
285void process_arg(char* arg, std::function<char *()> next, exec_options &options)275void process_arg(char* arg, std::function<char *()> next, exec_options &options)
286{276{
287 assert(arg != nullptr && next != nullptr);277 assert(arg != nullptr && next != nullptr);
@@ -326,28 +316,30 @@ void process_arg(char* arg, std::function<char *()> next, exec_options &options) · 38 unmodified lines
326 if (sys_threads <= 0 || 4 < sys_threads)316 if (sys_threads <= 0 || 4 < sys_threads)
327 throw arg_exception_t{ "Invalid system thread value", sys_threads_str };317 throw arg_exception_t{ "Invalid system thread value", sys_threads_str };
328 318
329 options.sys_thread_count = sys_threads;319 options.vm_config.sys_thread_pool_size = sys_threads;
330 }320 }
331 break;321 break;
332 322
333 case 'l':323 case 'l':
324 // Set the callback
325 debug::set_logging_callback(log_callback);
334 for (auto i = std::size_t{ 2 }; i < arg_len; ++i)326 for (auto i = std::size_t{ 2 }; i < arg_len; ++i)
335 {327 {
336 switch (arg[i])328 switch (arg[i])
337 {329 {
338 case 's': enable_logging(debug::component_trace_t::scheduler);330 case 's': debug::set_component_tracing(debug::component_trace_t::scheduler, true);
339 break;331 break;
340 case 'S': enable_logging(debug::component_trace_t::stack);332 case 'S': debug::set_component_tracing(debug::component_trace_t::stack, true);
341 break;333 break;
342 case 't': enable_logging(debug::component_trace_t::thread);334 case 't': debug::set_component_tracing(debug::component_trace_t::thread, true);
343 break;335 break;
344 case 'T': enable_logging(debug::component_trace_t::tool);336 case 'T': debug::set_component_tracing(debug::component_trace_t::tool, true);
345 break;337 break;
346 case 'e': enable_logging(debug::component_trace_t::exception);338 case 'e': debug::set_component_tracing(debug::component_trace_t::exception, true);
347 break;339 break;
348 case 'g': enable_logging(debug::component_trace_t::garbage_collector);340 case 'g': debug::set_component_tracing(debug::component_trace_t::garbage_collector, true);
349 break;341 break;
350 case 'm': enable_logging(debug::component_trace_t::memory);342 case 'm': debug::set_component_tracing(debug::component_trace_t::memory, true);
351 break;343 break;
352 }344 }
353 }345 }
@@ -402,7 +394,7 @@ int main(int argc, char* argv[]) · 48 unmodified lines
402 return EXIT_SUCCESS;394 return EXIT_SUCCESS;
403 }395 }
404 396
405 vm_t vm{ options.sys_thread_count, options.thread_quanta };397 vm_t vm{ std::move(options.vm_config) };
406 398
407 if (options.enabled_debugger)399 if (options.enabled_debugger)
408 vm.load_tool(std::make_shared<debugger>(options.debugger));400 vm.load_tool(std::make_shared<debugger>(options.debugger));
src/include/disvm.h+35−12
@@ -7,18 +7,17 @@ · 6 unmodified lines
7#ifndef _DISVM_SRC_INCLUDE_DISVM_H_7#ifndef _DISVM_SRC_INCLUDE_DISVM_H_
8#define _DISVM_SRC_INCLUDE_DISVM_H_8#define _DISVM_SRC_INCLUDE_DISVM_H_
9 9
10#include "runtime.h"10#include <array>
11#include <iosfwd>11#include <iosfwd>
12#include <memory>12#include <memory>
13#include <functional>13#include <functional>
14#include <mutex>14#include <mutex>
15#include <forward_list>15#include <forward_list>
16#include "runtime.h"
17#include "exceptions.h"
16 18
17namespace disvm19namespace disvm
18{20{
19 // Read in the module at the supplied path
20 std::unique_ptr<runtime::vm_module_t> read_module(const char *path);
21
22 // Read in a module from the supplied stream21 // Read in a module from the supplied stream
23 std::unique_ptr<runtime::vm_module_t> read_module(std::istream &data);22 std::unique_ptr<runtime::vm_module_t> read_module(std::istream &data);
24 23
@@ -51,20 +50,37 @@ namespace disvm · 26 unmodified lines
51 // Callback for enumerating loaded modules - return 'false' to stop enumeration, otherwise 'true'50 // Callback for enumerating loaded modules - return 'false' to stop enumeration, otherwise 'true'
52 using loaded_vm_module_callback_t = std::function<bool(const loaded_vm_module_t&)>;51 using loaded_vm_module_callback_t = std::function<bool(const loaded_vm_module_t&)>;
53 52
53 // Configuration for the created VM
54 class vm_config_t final
55 {
56 public:
57 vm_config_t();
58 vm_config_t(vm_config_t &&);
59
60 // Settings for the default scheduler. Only used if create_scheduler is not set.
61 // Both of these values are initialized to valid default values on construction.
62 uint32_t sys_thread_pool_size;
63 uint32_t thread_quanta;
64
65 create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler;
66 create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc;
67
68 // Prefix paths used by the default module resolver if the path supplied by
69 // the user isn't found. Note these will be concatenated with the supplied
70 // path, not other manipulation will be performed.
71 std::vector<std::string> probing_paths;
72 std::vector<std::unique_ptr<runtime::vm_module_resolver_t>> additional_resolvers;
73 };
74
54 // Virtual machine75 // Virtual machine
55 class vm_t final76 class vm_t final
56 {77 {
57 public: // static78 public: // static
58 static const uint32_t root_vm_thread_id;79 static const uint32_t root_vm_thread_id;
59 static const uint32_t default_system_thread_count;
60 static const uint32_t default_thread_quanta;
61 80
62 public:81 public:
63 explicit vm_t(82 explicit vm_t();
64 create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler = nullptr,83 explicit vm_t(vm_config_t config);
65 create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc = nullptr);
66
67 explicit vm_t(uint32_t thread_pool_size, uint32_t thread_quanta);
68 84
69 ~vm_t();85 ~vm_t();
70 86
@@ -92,7 +108,7 @@ namespace disvm · 21 unmodified lines
92 // Enumerate all loaded modules - this can include references to modules that108 // Enumerate all loaded modules - this can include references to modules that
93 // have been unloaded due to no longer being used. If the module instance is null, the109 // have been unloaded due to no longer being used. If the module instance is null, the
94 // module is no longer loaded.110 // module is no longer loaded.
95 // Note this will only enumerate modules that have been loaded by a call to vm_t::load_module.111 // Note this will only enumerate modules that have been loaded via a path.
96 void enum_loaded_modules(loaded_vm_module_callback_t callback) const;112 void enum_loaded_modules(loaded_vm_module_callback_t callback) const;
97 113
98 // Access the scheduler control for this VM.114 // Access the scheduler control for this VM.
@@ -118,10 +134,17 @@ namespace disvm · 19 unmodified lines
118 mutable std::mutex _modules_lock;134 mutable std::mutex _modules_lock;
119 loaded_modules_t _modules;135 loaded_modules_t _modules;
120 136
137 std::vector<std::unique_ptr<runtime::vm_module_resolver_t>> _module_resolvers;
121 std::unique_ptr<runtime::vm_scheduler_t> _scheduler;138 std::unique_ptr<runtime::vm_scheduler_t> _scheduler;
122 std::unique_ptr<runtime::vm_garbage_collector_t> _gc;139 std::unique_ptr<runtime::vm_garbage_collector_t> _gc;
123 std::unique_ptr<runtime::vm_tool_dispatch_t> _tool_dispatch;140 std::unique_ptr<runtime::vm_tool_dispatch_t> _tool_dispatch;
124 std::mutex _tool_dispatch_lock;141 std::mutex _tool_dispatch_lock;
142
143 std::atomic_flag _last_syscall_error_message_lock;
144 std::array<char, 128> _last_syscall_error_message;
145
146 friend void disvm::runtime::push_syscall_error_message(disvm::vm_t &, const char *) noexcept;
147 friend std::string disvm::runtime::pop_syscall_error_message(disvm::vm_t &);
125 };148 };
126}149}
127 150
src/include/exceptions.h+14−4
@@ -16,6 +16,10 @@ namespace disvm · 15 unmodified lines
16{16{
17 namespace runtime17 namespace runtime
18 {18 {
19 // Functions for managing syscall exception messages
20 void push_syscall_error_message(disvm::vm_t &, const char *) noexcept;
21 std::string pop_syscall_error_message(disvm::vm_t &);
22
19 class vm_user_exception : public std::runtime_error23 class vm_user_exception : public std::runtime_error
20 {24 {
21 public:25 public:
@@ -32,6 +36,16 @@ namespace disvm · 10 unmodified lines
32 { }36 { }
33 };37 };
34 38
39 class vm_syscall_exception : public vm_system_exception
40 {
41 public:
42 explicit vm_syscall_exception(disvm::vm_t &vm, const char *message)
43 : vm_system_exception(message)
44 {
45 push_syscall_error_message(vm, message);
46 }
47 };
48
35 class vm_module_exception : public vm_system_exception49 class vm_module_exception : public vm_system_exception
36 {50 {
37 public:51 public:
@@ -43,10 +57,6 @@ namespace disvm · 5 unmodified lines
43 class module_reader_exception final : public vm_module_exception57 class module_reader_exception final : public vm_module_exception
44 {58 {
45 public:59 public:
46 explicit module_reader_exception()
47 : vm_module_exception("Invalid module")
48 { }
49
50 explicit module_reader_exception(const char *message)60 explicit module_reader_exception(const char *message)
51 : vm_module_exception(message)61 : vm_module_exception(message)
52 { }62 { }
src/include/runtime.h+13−0
@@ -880,6 +880,19 @@ namespace disvm · 879 unmodified lines
880 // Create a garbage collector that does nothing.880 // Create a garbage collector that does nothing.
881 std::unique_ptr<vm_garbage_collector_t> create_no_op_gc(vm_t &);881 std::unique_ptr<vm_garbage_collector_t> create_no_op_gc(vm_t &);
882 882
883 // VM module path resolver interface
884 class vm_module_resolver_t
885 {
886 public:
887 virtual ~vm_module_resolver_t() = 0;
888
889 // Try to resolve the supplied path to a module.
890 // Implementers of this interface should never explicitly throw
891 // an exception and instead return 'false'. Exception thrown
892 // indirectly by calling supplied DisVM functions are valid.
893 virtual bool try_resolve_module(const char *path, std::unique_ptr<vm_module_t> &new_module) = 0;
894 };
895
883 // Forward declaration896 // Forward declaration
884 class vm_tool_controller_t;897 class vm_tool_controller_t;
885 898
src/include/utils.h+23−17
@@ -11,6 +11,7 @@ · 10 unmodified lines
11#include <type_traits>11#include <type_traits>
12#include <string>12#include <string>
13#include <cstring>13#include <cstring>
14#include <cassert>
14#include <vector>15#include <vector>
15 16
16namespace disvm17namespace disvm
@@ -36,34 +37,39 @@ namespace disvm · 19 unmodified lines
36 }37 }
37 38
38 // split the string using the delimiters contained in the supplied string39 // split the string using the delimiters contained in the supplied string
39 inline std::vector<std::string> split(const std::string &str, const char *delims)40 inline std::vector<std::string> split(const char *str, const char *delims)
40 {41 {
41 auto split_string = std::vector<std::string>{};42 assert(str != nullptr);
42 43
43 auto curr = str.begin();44 std::vector<std::string> parts;
44 auto end = str.cend();45 auto begin = str;
45 while (curr != end)46 while (*str != '\0')
46 {47 {
47 auto curr_begin = curr;48 auto curr = str++;
48 49 // Check for any delimiter
49 while (curr != end && (nullptr == std::strchr(delims, *curr)))50 if (nullptr != std::strchr(delims, *curr))
50 ++curr;51 {
52 if (begin != curr)
53 parts.push_back({ begin, curr });
51 54
52 if (curr_begin != curr)55 begin = str;
53 split_string.push_back(std::move(std::string{ curr_begin, curr }));56 }
57 }
54 58
55 if (curr == end)59 if (begin != str)
56 break;60 parts.push_back({ begin, str });
57 61
58 ++curr;62 return parts;
59 }63 }
60 64
61 return split_string;65 inline std::vector<std::string> split(const std::string &str, const char *delims)
66 {
67 return split(str.c_str(), delims);
62 }68 }
63 69
64 inline std::vector<std::string> split(const std::string &str)70 inline std::vector<std::string> split(const std::string &str)
65 {71 {
66 return split(str, " ");72 return split(str.c_str(), " ");
67 }73 }
68 }74 }
69}75}
src/vm/disvm.gyp+1−0
@@ -13,6 +13,7 @@ · 12 unmodified lines
13 'list.cpp',13 'list.cpp',
14 'module_reader.cpp',14 'module_reader.cpp',
15 'module_ref.cpp',15 'module_ref.cpp',
16 'module_resolver.cpp',
16 'scheduler.cpp',17 'scheduler.cpp',
17 'stack.cpp',18 'stack.cpp',
18 'string.cpp',19 'string.cpp',
src/vm/execution_table.cpp+0−1
@@ -1773,7 +1773,6 @@ namespace · 1772 unmodified lines
1773 catch (const vm_module_exception &e)1773 catch (const vm_module_exception &e)
1774 {1774 {
1775 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "exception: load: >>%s<<", e.what());1775 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "exception: load: >>%s<<", e.what());
1776 assert(false && "Failure reading module");
1777 return;1776 return;
1778 }1777 }
1779 }1778 }
src/vm/module_reader.cpp+1−15
@@ -8,7 +8,7 @@ · 7 unmodified lines
8#include <limits>8#include <limits>
9#include <memory>9#include <memory>
10#include <iostream>10#include <iostream>
11#include <fstream>11#include <sstream>
12#include <array>12#include <array>
13#include <tuple>13#include <tuple>
14#include <disvm.h>14#include <disvm.h>
@@ -739,20 +739,6 @@ vm_module_t::~vm_module_t() · 724 unmodified lines
739 original_mp->release();739 original_mp->release();
740}740}
741 741
742std::unique_ptr<vm_module_t> disvm::read_module(const char *path)
743{
744 assert(path != nullptr);
745
746 auto module_file = std::ifstream{ path, std::ifstream::binary };
747 if (!module_file.is_open())
748 {
749 assert(false && "Failed to open module");
750 throw vm_user_exception{ "Failed to open module" };
751 }
752
753 return read_module(module_file);
754}
755
756std::unique_ptr<runtime::vm_module_t> disvm::read_module(std::istream &data)742std::unique_ptr<runtime::vm_module_t> disvm::read_module(std::istream &data)
757{743{
758 util::buffered_reader_t reader{ data };744 util::buffered_reader_t reader{ data };
src/vm/module_resolver.cpp+72−0
@@ -0,0 +1,72 @@
1//
2// Dis VM
3// File: module_resolver.cpp
4// Author: arr
5//
6
7#include <cassert>
8#include <fstream>
9#include <sstream>
10#include <disvm.h>
11#include <debug.h>
12#include <exceptions.h>
13#include "module_resolver.h"
14
15using namespace disvm::runtime;
16
17// Empty destructor for vm module resolver 'interface'
18vm_module_resolver_t::~vm_module_resolver_t()
19{ }
20
21default_resolver_t::default_resolver_t(disvm::vm_t &vm)
22 : _vm{ vm }
23{ }
24
25default_resolver_t::default_resolver_t(disvm::vm_t &vm, std::vector<std::string> probing_paths)
26 : _probing_paths{ std::move(probing_paths) }
27 , _vm{ vm }
28{ }
29
30default_resolver_t::~default_resolver_t()
31{ }
32
33bool default_resolver_t::try_resolve_module(const char *path, std::unique_ptr<vm_module_t> &new_module)
34{
35 assert(path != nullptr);
36
37 auto log = debug::is_component_tracing_enabled<debug::component_trace_t::module>();
38 if (log)
39 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "resolve: try module path: >>%s<<", path);
40
41 auto module_file = std::ifstream{ path, std::ifstream::binary };
42 if (!module_file.is_open())
43 {
44 // The raw path isn't valid, try using probing paths
45 for (auto p : _probing_paths)
46 {
47 p.append(path);
48 if (log)
49 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "resolve: try module path: >>%s<<", p.c_str());
50
51 module_file = std::ifstream{ p, std::ifstream::binary };
52 if (module_file.is_open())
53 {
54 if (log)
55 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "resolve: successful modified module path: >>%s<< >>%s<<", path, p.c_str());
56
57 break;
58 }
59 }
60
61 if (!module_file.is_open())
62 {
63 push_syscall_error_message(_vm, "Unable to resolve path");
64 return false;
65 }
66 }
67
68 new_module = disvm::read_module(module_file);
69 assert(new_module != nullptr);
70
71 return true;
72}
src/vm/module_resolver.h+37−0
@@ -0,0 +1,37 @@
1//
2// Dis VM
3// File: module_resolver.h
4// Author: arr
5//
6
7#ifndef _DISVM_SRC_VM_MODULE_RESOLVER_H_
8#define _DISVM_SRC_VM_MODULE_RESOLVER_H_
9
10#include <cstdint>
11#include <string>
12#include <vector>
13#include <runtime.h>
14
15namespace disvm
16{
17 namespace runtime
18 {
19 // Default module resolver
20 class default_resolver_t final : public vm_module_resolver_t
21 {
22 public:
23 default_resolver_t(disvm::vm_t &vm);
24 default_resolver_t(disvm::vm_t &vm, std::vector<std::string> probing_paths);
25 ~default_resolver_t();
26
27 public: // vm_module_resolver_t
28 bool try_resolve_module(const char *path, std::unique_ptr<vm_module_t> &new_module);
29
30 private:
31 const std::vector<std::string> _probing_paths;
32 disvm::vm_t &_vm;
33 };
34 }
35}
36
37#endif // _DISVM_SRC_VM_MODULE_RESOLVER_H_
src/vm/scheduler.cpp+0−2
@@ -431,8 +431,6 @@ bool default_scheduler_t::enqueue_thread_unsafe(std::shared_ptr<thread_instance_ · 430 unmodified lines
431 throw vm_term_request{ err_msg };431 throw vm_term_request{ err_msg };
432 }432 }
433 433
434 case vm_thread_state_t::release:
435 case vm_thread_state_t::running:
436 default:434 default:
437 assert(false && "Unexpected thread state");435 assert(false && "Unexpected thread state");
438 throw vm_system_exception{ "Unexpected thread state" };436 throw vm_system_exception{ "Unexpected thread state" };
src/vm/sys/Sysmod.cpp+35−17
@@ -166,26 +166,26 @@ namespace · 165 unmodified lines
166 return _fd_path;166 return _fd_path;
167 }167 }
168 168
169 word_t read(const word_t buffer_size_in_bytes, void *buffer)169 word_t read(disvm::vm_t &vm, const word_t buffer_size_in_bytes, void *buffer)
170 {170 {
171 if (_istream == nullptr)171 if (_istream == nullptr)
172 throw vm_system_exception{ "File descriptor not open for read operation" };172 throw vm_system_exception{ "File descriptor not open for read operation" };
173 173
174 _istream->read(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));174 _istream->read(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));
175 if (_istream->bad())175 if (_istream->bad())
176 throw vm_user_exception{ "Read operation error" };176 throw vm_syscall_exception{ vm, "Read operation error" };
177 177
178 return static_cast<word_t>(_istream->gcount());178 return static_cast<word_t>(_istream->gcount());
179 }179 }
180 180
181 void write(const word_t buffer_size_in_bytes, void *buffer)181 void write(disvm::vm_t &vm, const word_t buffer_size_in_bytes, void *buffer)
182 {182 {
183 if (_ostream == nullptr)183 if (_ostream == nullptr)
184 throw vm_system_exception{ "File descriptor not open for write operation" };184 throw vm_system_exception{ "File descriptor not open for write operation" };
185 185
186 _ostream->write(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));186 _ostream->write(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));
187 if (_ostream->bad())187 if (_ostream->bad())
188 throw vm_user_exception{ "Write operation error" };188 throw vm_syscall_exception{ vm, "Write operation error" };
189 }189 }
190 190
191 big_t seek(const std::ios::seekdir start, const big_t offset)191 big_t seek(const std::ios::seekdir start, const big_t offset)
@@ -470,7 +470,16 @@ void · 278 unmodified lines
470Sys_fildes(vm_registers_t &r, vm_t &vm)470Sys_fildes(vm_registers_t &r, vm_t &vm)
471{471{
472 auto &fp = r.stack.peek_frame()->base<F_Sys_fildes>();472 auto &fp = r.stack.peek_frame()->base<F_Sys_fildes>();
473 throw vm_system_exception{ "Instruction not implemented" };473
474 dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));
475 *fp.ret = nullptr;
476
477 auto fd = sys::fetch_fd_record(fp.fd);
478 if (fd != nullptr)
479 {
480 fd->add_ref();
481 *fp.ret = fd->get_allocation();
482 }
474}483}
475 484
476void485void
@@ -608,16 +617,16 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 131 unmodified lines
608 617
609 auto static_buffer = std::array<char, 1024>{};618 auto static_buffer = std::array<char, 1024>{};
610 auto msg_args = &fp.vargs;619 auto msg_args = &fp.vargs;
611 auto written = sys::printf_to_buffer(*str, msg_args, fp_base, static_buffer.size(), static_buffer.data());620 auto written = sys::printf_to_buffer(vm, *str, msg_args, fp_base, static_buffer.size(), static_buffer.data());
612 if (written >= 0)621 if (written >= 0)
613 {622 {
614 fd_stdout->write(written, static_buffer.data());623 fd_stdout->write(vm, written, static_buffer.data());
615 }624 }
616 else625 else
617 {626 {
618 auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2);627 auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2);
619 written = sys::printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer);628 written = sys::printf_to_dynamic_buffer(vm, *str, msg_args, fp_base, dynamic_buffer);
620 fd_stdout->write(written, dynamic_buffer.data());629 fd_stdout->write(vm, written, dynamic_buffer.data());
621 }630 }
622 631
623 *fp.ret = written;632 *fp.ret = written;
@@ -657,7 +666,7 @@ Sys_read(vm_registers_t &r, vm_t &vm) · 33 unmodified lines
657 assert(fd_alloc->alloc_type == T_FD);666 assert(fd_alloc->alloc_type == T_FD);
658 667
659 auto fd = fd_alloc->get_allocation<vm_fd_t>();668 auto fd = fd_alloc->get_allocation<vm_fd_t>();
660 *fp.ret = fd->read(n, buffer->at(0));669 *fp.ret = fd->read(vm, n, buffer->at(0));
661}670}
662 671
663void672void
@@ -729,19 +738,28 @@ namespace · 65 unmodified lines
729 bool is_delim(const runtime::rune_t c, const vm_string_t &delim)738 bool is_delim(const runtime::rune_t c, const vm_string_t &delim)
730 {739 {
731 const auto max = delim.get_length();740 const auto max = delim.get_length();
732 assert(max > 0);
733 741
734 // Optimization for 3 or less delimiters742 // Optimization for 3 or less delimiters
735 switch (max)743 switch (max)
736 {744 {
737 default:745 default:
738 break;746 break;
739 case 1:
740 return (delim.get_rune(0) == c);
741 case 2:
742 return (delim.get_rune(0) == c) || (delim.get_rune(1) == c);
743 case 3:747 case 3:
744 return (delim.get_rune(0) == c) || (delim.get_rune(1) == c) || (delim.get_rune(2) == c);748 if (delim.get_rune(2) != c)
749 {
750 case 2:
751 if (delim.get_rune(1) != c)
752 {
753 case 1:
754 if (delim.get_rune(0) != c)
755 {
756 case 0:
757 return false;
758 }
759 }
760 }
761
762 return true;
745 }763 }
746 764
747 for (auto i = word_t{ 0 }; i < max; ++i)765 for (auto i = word_t{ 0 }; i < max; ++i)
@@ -900,7 +918,7 @@ Sys_write(vm_registers_t &r, vm_t &vm) · 152 unmodified lines
900 assert(fd_alloc->alloc_type == T_FD);918 assert(fd_alloc->alloc_type == T_FD);
901 919
902 auto fd = fd_alloc->get_allocation<vm_fd_t>();920 auto fd = fd_alloc->get_allocation<vm_fd_t>();
903 fd->write(n, buffer->at(0));921 fd->write(vm, n, buffer->at(0));
904 922
905 *fp.ret = n;923 *fp.ret = n;
906}924}
src/vm/sys/fd_manager.cpp+1−1
@@ -64,7 +64,7 @@ vm_alloc_t * disvm::runtime::sys::fetch_fd_record(word_t fd) · 63 unmodified lines
64 std::lock_guard<std::mutex> lock{ fd_map_lock };64 std::lock_guard<std::mutex> lock{ fd_map_lock };
65 auto iter = fd_map.find(fd);65 auto iter = fd_map.find(fd);
66 if (iter == fd_map.cend())66 if (iter == fd_map.cend())
67 throw vm_user_exception{ "Unknown file descriptor" };67 return nullptr;
68 68
69 return iter->second;69 return iter->second;
70 }70 }
src/vm/sys/print.cpp+12−4
@@ -30,8 +30,9 @@ namespace · 29 unmodified lines
30}30}
31 31
32// Shamelessly adopted and altered from Inferno implementation (xprint - libinterp/runt.c)32// Shamelessly adopted and altered from Inferno implementation (xprint - libinterp/runt.c)
33// [TODO] Handle UTF-833// [TODO] Handle UTF-8, binary format 'B'
34word_t disvm::runtime::sys::printf_to_buffer(34word_t disvm::runtime::sys::printf_to_buffer(
35 disvm::vm_t &vm,
35 const disvm::runtime::vm_string_t &msg_fmt,36 const disvm::runtime::vm_string_t &msg_fmt,
36 disvm::runtime::byte_t *msg_args,37 disvm::runtime::byte_t *msg_args,
37 disvm::runtime::pointer_t base,38 disvm::runtime::pointer_t base,
@@ -101,8 +102,8 @@ word_t disvm::runtime::sys::printf_to_buffer( · 63 unmodified lines
101 }102 }
102 case 'o': // octal103 case 'o': // octal
103 case 'd': // decimal104 case 'd': // decimal
104 case 'x': // hexidecimal105 case 'x': // hexadecimal
105 case 'X': // Upper-case hexidecimal106 case 'X': // Upper-case hexadecimal
106 format.push_back(static_cast<char>(c));107 format.push_back(static_cast<char>(c));
107 format.push_back('\0');108 format.push_back('\0');
108 if (big_flag)109 if (big_flag)
@@ -130,6 +131,12 @@ word_t disvm::runtime::sys::printf_to_buffer( · 21 unmodified lines
130 msg_args += sizeof(p);131 msg_args += sizeof(p);
131 break;132 break;
132 }133 }
134 case 'r': // Last system error message
135 {
136 auto err_msg = pop_syscall_error_message(vm);
137 wb = std::snprintf(b_curr, (b_end - b_curr), "%s", err_msg.c_str());
138 break;
139 }
133 // [SPEC] Undocumented format verb for debugging VM objects.140 // [SPEC] Undocumented format verb for debugging VM objects.
134 case 'H':141 case 'H':
135 {142 {
@@ -163,13 +170,14 @@ word_t disvm::runtime::sys::printf_to_buffer( · 27 unmodified lines
163}170}
164 171
165word_t disvm::runtime::sys::printf_to_dynamic_buffer(172word_t disvm::runtime::sys::printf_to_dynamic_buffer(
173 disvm::vm_t &vm,
166 const disvm::runtime::vm_string_t &msg_fmt,174 const disvm::runtime::vm_string_t &msg_fmt,
167 disvm::runtime::byte_t *msg_args,175 disvm::runtime::byte_t *msg_args,
168 disvm::runtime::pointer_t base,176 disvm::runtime::pointer_t base,
169 std::vector<char> &buffer)177 std::vector<char> &buffer)
170{178{
171 auto result = word_t{ 0 };179 auto result = word_t{ 0 };
172 while (-1 == (result = printf_to_buffer(msg_fmt, msg_args, base, buffer.size(), buffer.data())))180 while (-1 == (result = printf_to_buffer(vm, msg_fmt, msg_args, base, buffer.size(), buffer.data())))
173 buffer.resize(buffer.size() * 2);181 buffer.resize(buffer.size() * 2);
174 182
175 return result;183 return result;
src/vm/sys/sys_utils.h+3−1
@@ -22,6 +22,7 @@ namespace disvm · 21 unmodified lines
22 // Prints the format and arguments to the supplied buffer.22 // Prints the format and arguments to the supplied buffer.
23 // Returns the number of bytes written.23 // Returns the number of bytes written.
24 word_t printf_to_buffer(24 word_t printf_to_buffer(
25 disvm::vm_t &vm,
25 const vm_string_t &msg_fmt,26 const vm_string_t &msg_fmt,
26 byte_t *msg_args,27 byte_t *msg_args,
27 pointer_t base,28 pointer_t base,
@@ -31,6 +32,7 @@ namespace disvm · 3 unmodified lines
31 // Prints the format and arguments to the supplied dynamic buffer.32 // Prints the format and arguments to the supplied dynamic buffer.
32 // Returns the number of bytes written.33 // Returns the number of bytes written.
33 word_t printf_to_dynamic_buffer(34 word_t printf_to_dynamic_buffer(
35 disvm::vm_t &vm,
34 const vm_string_t &msg_fmt,36 const vm_string_t &msg_fmt,
35 byte_t *msg_args,37 byte_t *msg_args,
36 pointer_t base,38 pointer_t base,
@@ -52,10 +54,10 @@ namespace disvm · 15 unmodified lines
52 // The first three FD records are optimized for lock-less access.54 // The first three FD records are optimized for lock-less access.
53 word_t create_fd_record(vm_alloc_t *fd_alloc);55 word_t create_fd_record(vm_alloc_t *fd_alloc);
54 56
57 // Returns null if the record does not exist.
55 vm_alloc_t * fetch_fd_record(word_t fd);58 vm_alloc_t * fetch_fd_record(word_t fd);
56 59
57 void drop_fd_record(word_t fd);60 void drop_fd_record(word_t fd);
58
59 }61 }
60 }62 }
61}63}
src/vm/vm.cpp+146−26
@@ -7,6 +7,7 @@ · 6 unmodified lines
7#include <cassert>7#include <cassert>
8#include <memory>8#include <memory>
9#include <algorithm>9#include <algorithm>
10#include <sstream>
10#include <disvm.h>11#include <disvm.h>
11#include <debug.h>12#include <debug.h>
12#include <runtime.h>13#include <runtime.h>
@@ -16,41 +17,107 @@ · 3 unmodified lines
16#include "scheduler.h"17#include "scheduler.h"
17#include "garbage_collector.h"18#include "garbage_collector.h"
18#include "tool_dispatch.h"19#include "tool_dispatch.h"
20#include "module_resolver.h"
19 21
20using namespace disvm;22using namespace disvm;
21using namespace disvm::runtime;23using namespace disvm::runtime;
22 24
25namespace
26{
27 // Ideally this would be custom assembly based on the CPU,
28 // but since this is just for the setting of the last error
29 // message there is already a large cost associated with the
30 // fact an error occurred so this is okay.
31 struct inefficient_spin_lock_t final
32 {
33 inefficient_spin_lock_t(std::atomic_flag &is_lock)
34 : _lf{ is_lock }
35 {
36 while (!_lf.test_and_set())
37 ;
38 }
39
40 ~inefficient_spin_lock_t()
41 {
42 _lf.clear();
43 }
44
45 private:
46 std::atomic_flag &_lf;
47 };
48}
49
50void disvm::runtime::push_syscall_error_message(disvm::vm_t &vm, const char *msg) noexcept
51{
52 inefficient_spin_lock_t sl{ vm._last_syscall_error_message_lock };
53
54 assert(msg != nullptr);
55 auto bytes_to_copy = std::min(std::strlen(msg), vm._last_syscall_error_message.size() - 1);
56 std::memcpy(vm._last_syscall_error_message.data(), msg, bytes_to_copy);
57
58 // Ensure there is always a null
59 vm._last_syscall_error_message[bytes_to_copy] = '\0';
60}
61
62std::string disvm::runtime::pop_syscall_error_message(disvm::vm_t &vm)
63{
64 inefficient_spin_lock_t sl{ vm._last_syscall_error_message_lock };
65
66 std::string msg{ vm._last_syscall_error_message.data() };
67 vm._last_syscall_error_message[0] = '\0';
68 return msg;
69}
70
23const uint32_t vm_t::root_vm_thread_id = 0;71const uint32_t vm_t::root_vm_thread_id = 0;
24const uint32_t vm_t::default_system_thread_count = 1;
25 72
26// The Inferno implementation defined the thread quanta as 2048 (include/interp.h)73// The Inferno implementation defined the thread quanta as 2048 (include/interp.h)
27const uint32_t vm_t::default_thread_quanta = 2048;74const uint32_t default_thread_quanta = 2048;
28 75const uint32_t default_system_thread_count = 1;
29vm_t::vm_t(76
30 create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler,77vm_config_t::vm_config_t()
31 create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc)78 : create_gc{ nullptr }
79 , create_scheduler{ nullptr }
80 , sys_thread_pool_size{ default_system_thread_count }
81 , thread_quanta{ default_thread_quanta }
82{ }
83
84vm_config_t::vm_config_t(vm_config_t &&other)
85 : additional_resolvers{ std::move(other.additional_resolvers) }
86 , create_gc{ other.create_gc }
87 , create_scheduler{ other.create_scheduler }
88 , probing_paths{ std::move(other.probing_paths) }
89 , sys_thread_pool_size{ other.sys_thread_pool_size }
90 , thread_quanta{ other.thread_quanta }
91{ }
92
93vm_t::vm_t()
94 : _last_syscall_error_message_lock{ ATOMIC_FLAG_INIT }
32{95{
33 // Initialize built-in modules.96 // Initialize built-in modules.
34 builtin::initialize_builtin_modules();97 builtin::initialize_builtin_modules();
35 98
36 if (create_gc == nullptr)99 _gc = std::make_unique<default_garbage_collector_t>(*this);
37 _gc = std::make_unique<default_garbage_collector_t>(*this);100 _scheduler = std::make_unique<default_scheduler_t>(*this, default_system_thread_count, default_thread_quanta);
38 else101 _module_resolvers.push_back(std::make_unique<default_resolver_t>(*this));
39 _gc = create_gc(*this);
40
41 if (create_scheduler == nullptr)
42 _scheduler = std::make_unique<default_scheduler_t>(*this, default_system_thread_count, default_thread_quanta);
43 else
44 _scheduler = create_scheduler(*this);
45}102}
46 103
47vm_t::vm_t(uint32_t system_thread_count, uint32_t thread_quanta)104vm_t::vm_t(vm_config_t config)
48{105{
49 // Initialize built-in modules.106 // Initialize built-in modules.
50 builtin::initialize_builtin_modules();107 builtin::initialize_builtin_modules();
51 108
52 _gc = std::make_unique<default_garbage_collector_t>(*this);109 if (config.create_gc == nullptr)
53 _scheduler = std::make_unique<default_scheduler_t>(*this, system_thread_count, thread_quanta);110 _gc = std::make_unique<default_garbage_collector_t>(*this);
111 else
112 _gc = config.create_gc(*this);
113
114 if (config.create_scheduler == nullptr)
115 _scheduler = std::make_unique<default_scheduler_t>(*this, config.sys_thread_pool_size, config.thread_quanta);
116 else
117 _scheduler = config.create_scheduler(*this);
118
119 _module_resolvers = std::move(config.additional_resolvers);
120 _module_resolvers.push_back(std::make_unique<default_resolver_t>(*this, std::move(config.probing_paths)));
54}121}
55 122
56vm_t::~vm_t()123vm_t::~vm_t()
@@ -74,7 +141,7 @@ vm_version_t vm_t::get_version() const · 17 unmodified lines
74 141
75namespace142namespace
76{143{
77 std::unique_ptr<runtime::vm_thread_t> _create_thread_safe(std::unique_ptr<runtime::vm_module_ref_t> &entry_module_ref)144 std::unique_ptr<runtime::vm_thread_t> _create_thread_safe(std::unique_ptr<runtime::vm_module_ref_t> entry_module_ref)
78 {145 {
79 auto thread = std::make_unique<vm_thread_t>(*entry_module_ref, disvm::vm_t::root_vm_thread_id);146 auto thread = std::make_unique<vm_thread_t>(*entry_module_ref, disvm::vm_t::root_vm_thread_id);
80 147
@@ -92,7 +159,7 @@ const runtime::vm_thread_t& vm_t::exec(const char *path) · 11 unmodified lines
92 auto entry_module = load_module(path);159 auto entry_module = load_module(path);
93 160
94 auto entry_module_ref = std::make_unique<vm_module_ref_t>(entry_module);161 auto entry_module_ref = std::make_unique<vm_module_ref_t>(entry_module);
95 auto thread = _create_thread_safe(entry_module_ref);162 auto thread = _create_thread_safe(std::move(entry_module_ref));
96 return schedule_thread(std::move(thread));163 return schedule_thread(std::move(thread));
97}164}
98 165
@@ -101,7 +168,7 @@ const runtime::vm_thread_t& vm_t::exec(std::unique_ptr<runtime::vm_module_t> ent · 2 unmodified lines
101 assert(entry_module != nullptr);168 assert(entry_module != nullptr);
102 169
103 auto entry_module_ref = std::make_unique<vm_module_ref_t>(std::move(entry_module));170 auto entry_module_ref = std::make_unique<vm_module_ref_t>(std::move(entry_module));
104 auto thread = _create_thread_safe(entry_module_ref);171 auto thread = _create_thread_safe(std::move(entry_module_ref));
105 return schedule_thread(std::move(thread));172 return schedule_thread(std::move(thread));
106}173}
107 174
@@ -112,12 +179,64 @@ const runtime::vm_thread_t& vm_t::fork( · 4 unmodified lines
112 runtime::vm_pc_t initial_pc)179 runtime::vm_pc_t initial_pc)
113{180{
114 if (initial_pc < 0 || static_cast<vm_pc_t>(module_ref.code_section.size()) <= initial_pc)181 if (initial_pc < 0 || static_cast<vm_pc_t>(module_ref.code_section.size()) <= initial_pc)
115 throw vm_system_exception{ "Invalid entry program counter" };182 throw vm_user_exception{ "Invalid entry program counter" };
116 183
117 auto thread = std::make_unique<vm_thread_t>(module_ref, parent_tid, initial_frame, initial_pc);184 auto thread = std::make_unique<vm_thread_t>(module_ref, parent_tid, initial_frame, initial_pc);
118 return schedule_thread(std::move(thread));185 return schedule_thread(std::move(thread));
119}186}
120 187
188namespace
189{
190 std::unique_ptr<vm_module_t> resolve_module_from_path(const char *path, const std::vector<std::unique_ptr<runtime::vm_module_resolver_t>> &resolvers)
191 {
192 assert(!resolvers.empty());
193 std::unique_ptr<vm_module_t> resolved_module;
194
195 // Check if the path is for the Inferno OS.
196 const char *inferno_root_path = "/dis/";
197 if (std::strncmp(path, inferno_root_path, sizeof(inferno_root_path)) == 0)
198 {
199 if (debug::is_component_tracing_enabled<debug::component_trace_t::module>())
200 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "load: vm module: Inferno OS path detected - >>%s<<", path);
201
202 // Plus 1 for the next character. We know this will either be
203 // null or valid character since we searched for a string
204 // containing the character above.
205 const char *module_name = std::strrchr(path, '/') + 1;
206
207 if (*module_name != '\0')
208 {
209 for (auto &r : resolvers)
210 {
211 // Since we are using a modified path, throwing an exception
212 // isn't fair. We will catch all exceptions here and rely on
213 // non-modified path resolution to trigger the actual failure.
214 try
215 {
216 if (r->try_resolve_module(module_name, resolved_module))
217 return resolved_module;
218 }
219 catch (...)
220 {
221 // No-op
222 }
223 }
224 }
225 }
226
227 // Attempt to resolve the path
228 for (auto &r : resolvers)
229 {
230 if (r->try_resolve_module(path, resolved_module))
231 return resolved_module;
232 }
233
234 std::stringstream ss;
235 ss << "Failed to resolve path to module: " << path;
236 throw vm_module_exception{ ss.str().c_str() };
237 }
238}
239
121std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path)240std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path)
122{241{
123 if (path == nullptr)242 if (path == nullptr)
@@ -139,12 +258,13 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 15 unmodified lines
139 // If an entry exists but is null, the module was loaded before. Read the module again and return.258 // If an entry exists but is null, the module was loaded before. Read the module again and return.
140 if (module == nullptr)259 if (module == nullptr)
141 {260 {
142 module = std::shared_ptr<vm_module_t>{ std::move(read_module(path)) };261 module = std::shared_ptr<vm_module_t>{ std::move(resolve_module_from_path(path, _module_resolvers)) };
143 module->vm_id = iter->vm_id;262 module->vm_id = iter->vm_id;
144 263
145 iter->module = module;264 iter->module = module;
146 265
147 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "reload: vm module: >>%s<<", path);266 if (debug::is_component_tracing_enabled<debug::component_trace_t::module>())
267 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "reload: vm module: >>%s<<", path);
148 }268 }
149 269
150 return module;270 return module;
@@ -159,8 +279,8 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 8 unmodified lines
159 }279 }
160 else280 else
161 {281 {
162 // Read in the module282 new_module = resolve_module_from_path(path, _module_resolvers);
163 new_module = std::move(read_module(path));283 assert(new_module != nullptr);
164 }284 }
165 285
166 auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path));286 auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path));
src/vm/vm_memory.cpp+9−2
@@ -283,7 +283,12 @@ vm_alloc_t::vm_alloc_t(std::shared_ptr<const type_descriptor_t> td) · 282 unmodified lines
283 283
284vm_alloc_t::~vm_alloc_t()284vm_alloc_t::~vm_alloc_t()
285{285{
286 assert(_ref_count == 0);286#ifndef NDEBUG
287 if (_ref_count != 0)
288 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::warning,
289 "vm alloc being destroy with non-zero reference count. "
290 "This could be okay if this is happening due to frame unwinding from an exception");
291#endif
287 292
288 auto finalizer = alloc_type->finalizer;293 auto finalizer = alloc_type->finalizer;
289 if (finalizer != type_descriptor_t::no_finalizer)294 if (finalizer != type_descriptor_t::no_finalizer)
@@ -291,7 +296,9 @@ vm_alloc_t::~vm_alloc_t() · 1 unmodified line
291 296
292 // Free pointer types297 // Free pointer types
293 destroy_memory(*alloc_type, get_allocation());298 destroy_memory(*alloc_type, get_allocation());
294 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: vm alloc");299
300 if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>())
301 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: vm alloc");
295}302}
296 303
297std::size_t vm_alloc_t::add_ref()304std::size_t vm_alloc_t::add_ref()