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
195195 , enabled_debugger{ false }
196196 , quiet_start{ false }
197197 , print_help{ false }
198− , sys_thread_count{ vm_t::default_system_thread_count }
199− , thread_quanta{ vm_t::default_thread_quanta }
198+ , vm_config{}
200199 { }
201200
202201 std::vector<char *> vm_args;
203202
204− uint32_t sys_thread_count;
205− uint32_t thread_quanta;
203+ vm_config_t vm_config;
206204
207205 bool print_help;
208206 bool quiet_start;
@@ -220,7 +218,7 @@ void print_banner(const exec_options &options) · 11 unmodified lines
220218 std::cout
221219 << "\n----------------\n"
222220 << "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";
224222
225223 if (options.enabled_debugger)
226224 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
274272 std::cout << ss.str();
275273 }
276274
277−void 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−
285275 void process_arg(char* arg, std::function<char *()> next, exec_options &options)
286276 {
287277 assert(arg != nullptr && next != nullptr);
@@ -326,28 +316,30 @@ void process_arg(char* arg, std::function<char *()> next, exec_options &options) · 38 unmodified lines
326316 if (sys_threads <= 0 || 4 < sys_threads)
327317 throw arg_exception_t{ "Invalid system thread value", sys_threads_str };
328318
329− options.sys_thread_count = sys_threads;
319+ options.vm_config.sys_thread_pool_size = sys_threads;
330320 }
331321 break;
332322
333323 case 'l':
324+ // Set the callback
325+ debug::set_logging_callback(log_callback);
334326 for (auto i = std::size_t{ 2 }; i < arg_len; ++i)
335327 {
336328 switch (arg[i])
337329 {
338− case 's': enable_logging(debug::component_trace_t::scheduler);
330+ case 's': debug::set_component_tracing(debug::component_trace_t::scheduler, true);
339331 break;
340− case 'S': enable_logging(debug::component_trace_t::stack);
332+ case 'S': debug::set_component_tracing(debug::component_trace_t::stack, true);
341333 break;
342− case 't': enable_logging(debug::component_trace_t::thread);
334+ case 't': debug::set_component_tracing(debug::component_trace_t::thread, true);
343335 break;
344− case 'T': enable_logging(debug::component_trace_t::tool);
336+ case 'T': debug::set_component_tracing(debug::component_trace_t::tool, true);
345337 break;
346− case 'e': enable_logging(debug::component_trace_t::exception);
338+ case 'e': debug::set_component_tracing(debug::component_trace_t::exception, true);
347339 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);
349341 break;
350− case 'm': enable_logging(debug::component_trace_t::memory);
342+ case 'm': debug::set_component_tracing(debug::component_trace_t::memory, true);
351343 break;
352344 }
353345 }
@@ -402,7 +394,7 @@ int main(int argc, char* argv[]) · 48 unmodified lines
402394 return EXIT_SUCCESS;
403395 }
404396
405− vm_t vm{ options.sys_thread_count, options.thread_quanta };
397+ vm_t vm{ std::move(options.vm_config) };
406398
407399 if (options.enabled_debugger)
408400 vm.load_tool(std::make_shared<debugger>(options.debugger));
src/include/disvm.h+35−12
@@ -7,18 +7,17 @@ · 6 unmodified lines
77 #ifndef _DISVM_SRC_INCLUDE_DISVM_H_
88 #define _DISVM_SRC_INCLUDE_DISVM_H_
99
10−#include "runtime.h"
10+#include <array>
1111 #include <iosfwd>
1212 #include <memory>
1313 #include <functional>
1414 #include <mutex>
1515 #include <forward_list>
16+#include "runtime.h"
17+#include "exceptions.h"
1618
1719 namespace disvm
1820 {
19− // Read in the module at the supplied path
20− std::unique_ptr<runtime::vm_module_t> read_module(const char *path);
21−
2221 // Read in a module from the supplied stream
2322 std::unique_ptr<runtime::vm_module_t> read_module(std::istream &data);
2423
@@ -51,20 +50,37 @@ namespace disvm · 26 unmodified lines
5150 // Callback for enumerating loaded modules - return 'false' to stop enumeration, otherwise 'true'
5251 using loaded_vm_module_callback_t = std::function<bool(const loaded_vm_module_t&)>;
5352
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+
5475 // Virtual machine
5576 class vm_t final
5677 {
5778 public: // static
5879 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;
6180
6281 public:
63− explicit vm_t(
64− create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler = nullptr,
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);
82+ explicit vm_t();
83+ explicit vm_t(vm_config_t config);
6884
6985 ~vm_t();
7086
@@ -92,7 +108,7 @@ namespace disvm · 21 unmodified lines
92108 // Enumerate all loaded modules - this can include references to modules that
93109 // have been unloaded due to no longer being used. If the module instance is null, the
94110 // 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.
96112 void enum_loaded_modules(loaded_vm_module_callback_t callback) const;
97113
98114 // Access the scheduler control for this VM.
@@ -118,10 +134,17 @@ namespace disvm · 19 unmodified lines
118134 mutable std::mutex _modules_lock;
119135 loaded_modules_t _modules;
120136
137+ std::vector<std::unique_ptr<runtime::vm_module_resolver_t>> _module_resolvers;
121138 std::unique_ptr<runtime::vm_scheduler_t> _scheduler;
122139 std::unique_ptr<runtime::vm_garbage_collector_t> _gc;
123140 std::unique_ptr<runtime::vm_tool_dispatch_t> _tool_dispatch;
124141 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 &);
125148 };
126149 }
127150
src/include/exceptions.h+14−4
@@ -16,6 +16,10 @@ namespace disvm · 15 unmodified lines
1616 {
1717 namespace runtime
1818 {
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+
1923 class vm_user_exception : public std::runtime_error
2024 {
2125 public:
@@ -32,6 +36,16 @@ namespace disvm · 10 unmodified lines
3236 { }
3337 };
3438
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+
3549 class vm_module_exception : public vm_system_exception
3650 {
3751 public:
@@ -43,10 +57,6 @@ namespace disvm · 5 unmodified lines
4357 class module_reader_exception final : public vm_module_exception
4458 {
4559 public:
46− explicit module_reader_exception()
47− : vm_module_exception("Invalid module")
48− { }
49−
5060 explicit module_reader_exception(const char *message)
5161 : vm_module_exception(message)
5262 { }
src/include/runtime.h+13−0
@@ -880,6 +880,19 @@ namespace disvm · 879 unmodified lines
880880 // Create a garbage collector that does nothing.
881881 std::unique_ptr<vm_garbage_collector_t> create_no_op_gc(vm_t &);
882882
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+
883896 // Forward declaration
884897 class vm_tool_controller_t;
885898
src/include/utils.h+23−17
@@ -11,6 +11,7 @@ · 10 unmodified lines
1111 #include <type_traits>
1212 #include <string>
1313 #include <cstring>
14+#include <cassert>
1415 #include <vector>
1516
1617 namespace disvm
@@ -36,34 +37,39 @@ namespace disvm · 19 unmodified lines
3637 }
3738
3839 // 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)
4041 {
41− auto split_string = std::vector<std::string>{};
42+ assert(str != nullptr);
4243
43− auto curr = str.begin();
44− auto end = str.cend();
45− while (curr != end)
44+ std::vector<std::string> parts;
45+ auto begin = str;
46+ while (*str != '\0')
4647 {
47− auto curr_begin = curr;
48−
49− while (curr != end && (nullptr == std::strchr(delims, *curr)))
50− ++curr;
48+ auto curr = str++;
49+ // Check for any delimiter
50+ if (nullptr != std::strchr(delims, *curr))
51+ {
52+ if (begin != curr)
53+ parts.push_back({ begin, curr });
5154
52− if (curr_begin != curr)
53− split_string.push_back(std::move(std::string{ curr_begin, curr }));
55+ begin = str;
56+ }
57+ }
5458
55− if (curr == end)
56− break;
59+ if (begin != str)
60+ parts.push_back({ begin, str });
5761
58− ++curr;
59− }
62+ return parts;
63+ }
6064
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);
6268 }
6369
6470 inline std::vector<std::string> split(const std::string &str)
6571 {
66− return split(str, " ");
72+ return split(str.c_str(), " ");
6773 }
6874 }
6975 }
src/vm/disvm.gyp+1−0
@@ -13,6 +13,7 @@ · 12 unmodified lines
1313 'list.cpp',
1414 'module_reader.cpp',
1515 'module_ref.cpp',
16+ 'module_resolver.cpp',
1617 'scheduler.cpp',
1718 'stack.cpp',
1819 'string.cpp',
src/vm/execution_table.cpp+0−1
@@ -1773,7 +1773,6 @@ namespace · 1772 unmodified lines
17731773 catch (const vm_module_exception &e)
17741774 {
17751775 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "exception: load: >>%s<<", e.what());
1776− assert(false && "Failure reading module");
17771776 return;
17781777 }
17791778 }
src/vm/module_reader.cpp+1−15
@@ -8,7 +8,7 @@ · 7 unmodified lines
88 #include <limits>
99 #include <memory>
1010 #include <iostream>
11−#include <fstream>
11+#include <sstream>
1212 #include <array>
1313 #include <tuple>
1414 #include <disvm.h>
@@ -739,20 +739,6 @@ vm_module_t::~vm_module_t() · 724 unmodified lines
739739 original_mp->release();
740740 }
741741
742−std::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−
756742 std::unique_ptr<runtime::vm_module_t> disvm::read_module(std::istream &data)
757743 {
758744 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+
15+using namespace disvm::runtime;
16+
17+// Empty destructor for vm module resolver 'interface'
18+vm_module_resolver_t::~vm_module_resolver_t()
19+{ }
20+
21+default_resolver_t::default_resolver_t(disvm::vm_t &vm)
22+ : _vm{ vm }
23+{ }
24+
25+default_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+
30+default_resolver_t::~default_resolver_t()
31+{ }
32+
33+bool 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+
15+namespace 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
431431 throw vm_term_request{ err_msg };
432432 }
433433
434− case vm_thread_state_t::release:
435− case vm_thread_state_t::running:
436434 default:
437435 assert(false && "Unexpected thread state");
438436 throw vm_system_exception{ "Unexpected thread state" };
src/vm/sys/Sysmod.cpp+35−17
@@ -166,26 +166,26 @@ namespace · 165 unmodified lines
166166 return _fd_path;
167167 }
168168
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)
170170 {
171171 if (_istream == nullptr)
172172 throw vm_system_exception{ "File descriptor not open for read operation" };
173173
174174 _istream->read(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));
175175 if (_istream->bad())
176− throw vm_user_exception{ "Read operation error" };
176+ throw vm_syscall_exception{ vm, "Read operation error" };
177177
178178 return static_cast<word_t>(_istream->gcount());
179179 }
180180
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)
182182 {
183183 if (_ostream == nullptr)
184184 throw vm_system_exception{ "File descriptor not open for write operation" };
185185
186186 _ostream->write(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));
187187 if (_ostream->bad())
188− throw vm_user_exception{ "Write operation error" };
188+ throw vm_syscall_exception{ vm, "Write operation error" };
189189 }
190190
191191 big_t seek(const std::ios::seekdir start, const big_t offset)
@@ -470,7 +470,16 @@ void · 278 unmodified lines
470470 Sys_fildes(vm_registers_t &r, vm_t &vm)
471471 {
472472 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+ }
474483 }
475484
476485 void
@@ -608,16 +617,16 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 131 unmodified lines
608617
609618 auto static_buffer = std::array<char, 1024>{};
610619 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());
612621 if (written >= 0)
613622 {
614− fd_stdout->write(written, static_buffer.data());
623+ fd_stdout->write(vm, written, static_buffer.data());
615624 }
616625 else
617626 {
618627 auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2);
619− written = sys::printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer);
620− fd_stdout->write(written, dynamic_buffer.data());
628+ written = sys::printf_to_dynamic_buffer(vm, *str, msg_args, fp_base, dynamic_buffer);
629+ fd_stdout->write(vm, written, dynamic_buffer.data());
621630 }
622631
623632 *fp.ret = written;
@@ -657,7 +666,7 @@ Sys_read(vm_registers_t &r, vm_t &vm) · 33 unmodified lines
657666 assert(fd_alloc->alloc_type == T_FD);
658667
659668 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));
661670 }
662671
663672 void
@@ -729,19 +738,28 @@ namespace · 65 unmodified lines
729738 bool is_delim(const runtime::rune_t c, const vm_string_t &delim)
730739 {
731740 const auto max = delim.get_length();
732− assert(max > 0);
733741
734742 // Optimization for 3 or less delimiters
735743 switch (max)
736744 {
737745 default:
738746 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);
743747 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;
745763 }
746764
747765 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
900918 assert(fd_alloc->alloc_type == T_FD);
901919
902920 auto fd = fd_alloc->get_allocation<vm_fd_t>();
903− fd->write(n, buffer->at(0));
921+ fd->write(vm, n, buffer->at(0));
904922
905923 *fp.ret = n;
906924 }
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
6464 std::lock_guard<std::mutex> lock{ fd_map_lock };
6565 auto iter = fd_map.find(fd);
6666 if (iter == fd_map.cend())
67− throw vm_user_exception{ "Unknown file descriptor" };
67+ return nullptr;
6868
6969 return iter->second;
7070 }
src/vm/sys/print.cpp+12−4
@@ -30,8 +30,9 @@ namespace · 29 unmodified lines
3030 }
3131
3232 // Shamelessly adopted and altered from Inferno implementation (xprint - libinterp/runt.c)
33−// [TODO] Handle UTF-8
33+// [TODO] Handle UTF-8, binary format 'B'
3434 word_t disvm::runtime::sys::printf_to_buffer(
35+ disvm::vm_t &vm,
3536 const disvm::runtime::vm_string_t &msg_fmt,
3637 disvm::runtime::byte_t *msg_args,
3738 disvm::runtime::pointer_t base,
@@ -101,8 +102,8 @@ word_t disvm::runtime::sys::printf_to_buffer( · 63 unmodified lines
101102 }
102103 case 'o': // octal
103104 case 'd': // decimal
104− case 'x': // hexidecimal
105− case 'X': // Upper-case hexidecimal
105+ case 'x': // hexadecimal
106+ case 'X': // Upper-case hexadecimal
106107 format.push_back(static_cast<char>(c));
107108 format.push_back('\0');
108109 if (big_flag)
@@ -130,6 +131,12 @@ word_t disvm::runtime::sys::printf_to_buffer( · 21 unmodified lines
130131 msg_args += sizeof(p);
131132 break;
132133 }
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+ }
133140 // [SPEC] Undocumented format verb for debugging VM objects.
134141 case 'H':
135142 {
@@ -163,13 +170,14 @@ word_t disvm::runtime::sys::printf_to_buffer( · 27 unmodified lines
163170 }
164171
165172 word_t disvm::runtime::sys::printf_to_dynamic_buffer(
173+ disvm::vm_t &vm,
166174 const disvm::runtime::vm_string_t &msg_fmt,
167175 disvm::runtime::byte_t *msg_args,
168176 disvm::runtime::pointer_t base,
169177 std::vector<char> &buffer)
170178 {
171179 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())))
173181 buffer.resize(buffer.size() * 2);
174182
175183 return result;
src/vm/sys/sys_utils.h+3−1
@@ -22,6 +22,7 @@ namespace disvm · 21 unmodified lines
2222 // Prints the format and arguments to the supplied buffer.
2323 // Returns the number of bytes written.
2424 word_t printf_to_buffer(
25+ disvm::vm_t &vm,
2526 const vm_string_t &msg_fmt,
2627 byte_t *msg_args,
2728 pointer_t base,
@@ -31,6 +32,7 @@ namespace disvm · 3 unmodified lines
3132 // Prints the format and arguments to the supplied dynamic buffer.
3233 // Returns the number of bytes written.
3334 word_t printf_to_dynamic_buffer(
35+ disvm::vm_t &vm,
3436 const vm_string_t &msg_fmt,
3537 byte_t *msg_args,
3638 pointer_t base,
@@ -52,10 +54,10 @@ namespace disvm · 15 unmodified lines
5254 // The first three FD records are optimized for lock-less access.
5355 word_t create_fd_record(vm_alloc_t *fd_alloc);
5456
57+ // Returns null if the record does not exist.
5558 vm_alloc_t * fetch_fd_record(word_t fd);
5659
5760 void drop_fd_record(word_t fd);
58−
5961 }
6062 }
6163 }
src/vm/vm.cpp+146−26
@@ -7,6 +7,7 @@ · 6 unmodified lines
77 #include <cassert>
88 #include <memory>
99 #include <algorithm>
10+#include <sstream>
1011 #include <disvm.h>
1112 #include <debug.h>
1213 #include <runtime.h>
@@ -16,41 +17,107 @@ · 3 unmodified lines
1617 #include "scheduler.h"
1718 #include "garbage_collector.h"
1819 #include "tool_dispatch.h"
20+#include "module_resolver.h"
1921
2022 using namespace disvm;
2123 using namespace disvm::runtime;
2224
25+namespace
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+
50+void 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+
62+std::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+
2371 const uint32_t vm_t::root_vm_thread_id = 0;
24−const uint32_t vm_t::default_system_thread_count = 1;
2572
2673 // The Inferno implementation defined the thread quanta as 2048 (include/interp.h)
27−const uint32_t vm_t::default_thread_quanta = 2048;
28−
29−vm_t::vm_t(
30− create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler,
31− create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc)
74+const uint32_t default_thread_quanta = 2048;
75+const uint32_t default_system_thread_count = 1;
76+
77+vm_config_t::vm_config_t()
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+
84+vm_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+
93+vm_t::vm_t()
94+ : _last_syscall_error_message_lock{ ATOMIC_FLAG_INIT }
3295 {
3396 // Initialize built-in modules.
3497 builtin::initialize_builtin_modules();
3598
36− if (create_gc == nullptr)
37− _gc = std::make_unique<default_garbage_collector_t>(*this);
38− else
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);
99+ _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);
101+ _module_resolvers.push_back(std::make_unique<default_resolver_t>(*this));
45102 }
46103
47−vm_t::vm_t(uint32_t system_thread_count, uint32_t thread_quanta)
104+vm_t::vm_t(vm_config_t config)
48105 {
49106 // Initialize built-in modules.
50107 builtin::initialize_builtin_modules();
51108
52− _gc = std::make_unique<default_garbage_collector_t>(*this);
53− _scheduler = std::make_unique<default_scheduler_t>(*this, system_thread_count, thread_quanta);
109+ if (config.create_gc == nullptr)
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)));
54121 }
55122
56123 vm_t::~vm_t()
@@ -74,7 +141,7 @@ vm_version_t vm_t::get_version() const · 17 unmodified lines
74141
75142 namespace
76143 {
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)
78145 {
79146 auto thread = std::make_unique<vm_thread_t>(*entry_module_ref, disvm::vm_t::root_vm_thread_id);
80147
@@ -92,7 +159,7 @@ const runtime::vm_thread_t& vm_t::exec(const char *path) · 11 unmodified lines
92159 auto entry_module = load_module(path);
93160
94161 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));
96163 return schedule_thread(std::move(thread));
97164 }
98165
@@ -101,7 +168,7 @@ const runtime::vm_thread_t& vm_t::exec(std::unique_ptr<runtime::vm_module_t> ent · 2 unmodified lines
101168 assert(entry_module != nullptr);
102169
103170 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));
105172 return schedule_thread(std::move(thread));
106173 }
107174
@@ -112,12 +179,64 @@ const runtime::vm_thread_t& vm_t::fork( · 4 unmodified lines
112179 runtime::vm_pc_t initial_pc)
113180 {
114181 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" };
116183
117184 auto thread = std::make_unique<vm_thread_t>(module_ref, parent_tid, initial_frame, initial_pc);
118185 return schedule_thread(std::move(thread));
119186 }
120187
188+namespace
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+
121240 std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path)
122241 {
123242 if (path == nullptr)
@@ -139,12 +258,13 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 15 unmodified lines
139258 // If an entry exists but is null, the module was loaded before. Read the module again and return.
140259 if (module == nullptr)
141260 {
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)) };
143262 module->vm_id = iter->vm_id;
144263
145264 iter->module = module;
146265
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);
148268 }
149269
150270 return module;
@@ -159,8 +279,8 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 8 unmodified lines
159279 }
160280 else
161281 {
162− // Read in the module
163− new_module = std::move(read_module(path));
282+ new_module = resolve_module_from_path(path, _module_resolvers);
283+ assert(new_module != nullptr);
164284 }
165285
166286 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
283283
284284 vm_alloc_t::~vm_alloc_t()
285285 {
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
287292
288293 auto finalizer = alloc_type->finalizer;
289294 if (finalizer != type_descriptor_t::no_finalizer)
@@ -291,7 +296,9 @@ vm_alloc_t::~vm_alloc_t() · 1 unmodified line
291296
292297 // Free pointer types
293298 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");
295302 }
296303
297304 std::size_t vm_alloc_t::add_ref()