/home/travis/build/MoarVM/MoarVM/src/moar.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | #include <platform/threads.h> |
3 | | #include "platform/random.h" |
4 | | #include "platform/time.h" |
5 | | #if defined(_MSC_VER) |
6 | | #define snprintf _snprintf |
7 | | #endif |
8 | | |
9 | | #ifndef _WIN32 |
10 | | # include <unistd.h> |
11 | | #else |
12 | | # include <process.h> |
13 | | #endif |
14 | | |
15 | 3.02k | #define init_mutex(loc, name) do { \ |
16 | 3.02k | if ((init_stat = uv_mutex_init(&loc)) < 0) { \ |
17 | 0 | fprintf(stderr, "MoarVM: Initialization of " name " mutex failed\n %s\n", \ |
18 | 0 | uv_strerror(init_stat)); \ |
19 | 0 | exit(1); \ |
20 | 0 | } \ |
21 | 3.02k | } while (0) |
22 | 720 | #define init_cond(loc, name) do { \ |
23 | 720 | if ((init_stat = uv_cond_init(&loc)) < 0) { \ |
24 | 0 | fprintf(stderr, "MoarVM: Initialization of " name " condition variable failed\n %s\n", \ |
25 | 0 | uv_strerror(init_stat)); \ |
26 | 0 | exit(1); \ |
27 | 0 | } \ |
28 | 720 | } while (0) |
29 | | |
30 | | static void setup_std_handles(MVMThreadContext *tc); |
31 | | |
32 | 0 | static FILE *fopen_perhaps_with_pid(char *env_var, char *path, const char *mode) { |
33 | 0 | FILE *result; |
34 | 0 | if (strstr(path, "%d")) { |
35 | 0 | MVMuint64 path_length = strlen(path); |
36 | 0 | MVMuint64 found_percents = 0; |
37 | 0 | MVMuint64 i; |
38 | 0 |
|
39 | 0 | /* Let's sanitize the format string a bit. Must only have |
40 | 0 | * a single printf-recognized directive. */ |
41 | 0 | for (i = 0; i < path_length; i++) { |
42 | 0 | if (path[i] == '%') { |
43 | 0 | /* %% is all right. */ |
44 | 0 | if (i + 1 < path_length && path[i + 1] == '%') { |
45 | 0 | i++; continue; |
46 | 0 | } |
47 | 0 | found_percents++; |
48 | 0 | } |
49 | 0 | } |
50 | 0 | /* We expect to pass only a single argument to snprintf here; |
51 | 0 | * just bail out if there's more than one directive. */ |
52 | 0 | if (found_percents > 1) { |
53 | 0 | result = fopen(path, mode); |
54 | 0 | } else { |
55 | 0 | char *fixed_path = malloc(path_length + 16); |
56 | 0 | MVMint64 pid; |
57 | 0 | #ifdef _WIN32 |
58 | | pid = _getpid(); |
59 | | #else |
60 | 0 | pid = getpid(); |
61 | 0 | #endif |
62 | 0 | /* We make the brave assumption that |
63 | 0 | * pids only go up to 16 characters. */ |
64 | 0 | snprintf(fixed_path, path_length + 16, path, pid); |
65 | 0 | result = fopen(fixed_path, mode); |
66 | 0 | free(fixed_path); |
67 | 0 | } |
68 | 0 | } else { |
69 | 0 | result = fopen(path, mode); |
70 | 0 | } |
71 | 0 |
|
72 | 0 | if (result) |
73 | 0 | return result; |
74 | 0 | fprintf(stderr, "MoarVM: Failed to open file `%s` given via `%s`: %s\n", |
75 | 0 | path, env_var, strerror(errno)); |
76 | 0 | exit(1); |
77 | 0 | } |
78 | | |
79 | | /* Create a new instance of the VM. */ |
80 | 144 | MVMInstance * MVM_vm_create_instance(void) { |
81 | 144 | MVMInstance *instance; |
82 | 144 | |
83 | 144 | char *spesh_log, *spesh_nodelay, *spesh_disable, *spesh_inline_disable, |
84 | 144 | *spesh_osr_disable, *spesh_limit, *spesh_blocking; |
85 | 144 | char *jit_log, *jit_expr_disable, *jit_disable, *jit_bytecode_dir, *jit_last_frame, *jit_last_bb; |
86 | 144 | char *dynvar_log; |
87 | 144 | int init_stat; |
88 | 144 | MVMuint32 hashSecret; |
89 | 144 | MVMuint64 now = MVM_platform_now(); |
90 | 144 | |
91 | 144 | /* Set up instance data structure. */ |
92 | 144 | instance = MVM_calloc(1, sizeof(MVMInstance)); |
93 | 144 | |
94 | 144 | /* Create the main thread's ThreadContext and stash it. */ |
95 | 144 | instance->main_thread = MVM_tc_create(NULL, instance); |
96 | 144 | MVM_getrandom(instance->main_thread, &hashSecret, sizeof(MVMuint32)); |
97 | 144 | instance->hashSecret ^= now; |
98 | 144 | instance->hashSecret ^= MVM_proc_getpid(instance->main_thread) * now; |
99 | 144 | instance->main_thread->thread_id = 1; |
100 | 144 | |
101 | 144 | /* Next thread to be created gets ID 2 (the main thread got ID 1). */ |
102 | 144 | MVM_store(&instance->next_user_thread_id, 2); |
103 | 144 | |
104 | 144 | /* Set up the permanent roots storage. */ |
105 | 144 | instance->num_permroots = 0; |
106 | 144 | instance->alloc_permroots = 16; |
107 | 144 | instance->permroots = MVM_malloc(sizeof(MVMCollectable **) * instance->alloc_permroots); |
108 | 144 | instance->permroot_descriptions = MVM_malloc(sizeof(char *) * instance->alloc_permroots); |
109 | 144 | init_mutex(instance->mutex_permroots, "permanent roots"); |
110 | 144 | |
111 | 144 | /* GC orchestration state. */ |
112 | 144 | init_mutex(instance->mutex_gc_orchestrate, "GC orchestration"); |
113 | 144 | init_cond(instance->cond_gc_start, "GC start"); |
114 | 144 | init_cond(instance->cond_gc_finish, "GC finish"); |
115 | 144 | init_cond(instance->cond_gc_intrays_clearing, "GC intrays clearing"); |
116 | 144 | init_cond(instance->cond_blocked_can_continue, "GC thread unblock"); |
117 | 144 | |
118 | 144 | /* Create fixed size allocator. */ |
119 | 144 | instance->fsa = MVM_fixed_size_create(instance->main_thread); |
120 | 144 | |
121 | 144 | /* Set up REPR registry mutex. */ |
122 | 144 | init_mutex(instance->mutex_repr_registry, "REPR registry"); |
123 | 144 | |
124 | 144 | /* Set up HLL config mutex. */ |
125 | 144 | init_mutex(instance->mutex_hllconfigs, "hll configs"); |
126 | 144 | |
127 | 144 | /* Set up DLL registry mutex. */ |
128 | 144 | init_mutex(instance->mutex_dll_registry, "REPR registry"); |
129 | 144 | |
130 | 144 | /* Set up extension registry mutex. */ |
131 | 144 | init_mutex(instance->mutex_ext_registry, "extension registry"); |
132 | 144 | |
133 | 144 | /* Set up extension op registry mutex. */ |
134 | 144 | init_mutex(instance->mutex_extop_registry, "extension op registry"); |
135 | 144 | |
136 | 144 | /* Set up SC registry mutex. */ |
137 | 144 | init_mutex(instance->mutex_sc_registry, "sc registry"); |
138 | 144 | |
139 | 144 | /* Set up loaded compunits hash mutex. */ |
140 | 144 | init_mutex(instance->mutex_loaded_compunits, "loaded compunits"); |
141 | 144 | |
142 | 144 | /* Set up container registry mutex. */ |
143 | 144 | init_mutex(instance->mutex_container_registry, "container registry"); |
144 | 144 | |
145 | 144 | /* Set up persistent object ID hash mutex. */ |
146 | 144 | init_mutex(instance->mutex_object_ids, "object ID hash"); |
147 | 144 | |
148 | 144 | /* Allocate all things during following setup steps directly in gen2, as |
149 | 144 | * they will have program lifetime. */ |
150 | 144 | MVM_gc_allocate_gen2_default_set(instance->main_thread); |
151 | 144 | |
152 | 144 | /* Set up integer constant and string cache. */ |
153 | 144 | init_mutex(instance->mutex_int_const_cache, "int constant cache"); |
154 | 144 | instance->int_const_cache = MVM_calloc(1, sizeof(MVMIntConstCache)); |
155 | 144 | instance->int_to_str_cache = MVM_calloc(MVM_INT_TO_STR_CACHE_SIZE, sizeof(MVMString *)); |
156 | 144 | |
157 | 144 | /* Initialize Unicode database and NFG. */ |
158 | 144 | MVM_unicode_init(instance->main_thread); |
159 | 144 | MVM_nfg_init(instance->main_thread); |
160 | 144 | |
161 | 144 | /* Bootstrap 6model. It is assumed the GC will not be called during this. */ |
162 | 144 | MVM_6model_bootstrap(instance->main_thread); |
163 | 144 | |
164 | 144 | /* Set up main thread's last_payload. */ |
165 | 144 | instance->main_thread->last_payload = instance->VMNull; |
166 | 144 | |
167 | 144 | /* Initialize event loop thread starting mutex. */ |
168 | 144 | init_mutex(instance->mutex_event_loop_start, "event loop thread start"); |
169 | 144 | |
170 | 144 | /* Create main thread object, and also make it the start of the all threads |
171 | 144 | * linked list. Set up the mutex to protect it. */ |
172 | 144 | instance->threads = instance->main_thread->thread_obj = (MVMThread *) |
173 | 144 | REPR(instance->boot_types.BOOTThread)->allocate( |
174 | 144 | instance->main_thread, STABLE(instance->boot_types.BOOTThread)); |
175 | 144 | instance->threads->body.stage = MVM_thread_stage_started; |
176 | 144 | instance->threads->body.tc = instance->main_thread; |
177 | 144 | instance->threads->body.native_thread_id = MVM_platform_thread_id(); |
178 | 144 | instance->threads->body.thread_id = instance->main_thread->thread_id; |
179 | 144 | init_mutex(instance->mutex_threads, "threads list"); |
180 | 144 | |
181 | 144 | /* Create compiler registry */ |
182 | 144 | instance->compiler_registry = MVM_repr_alloc_init(instance->main_thread, instance->boot_types.BOOTHash); |
183 | 144 | |
184 | 144 | /* Set up compiler registr mutex. */ |
185 | 144 | init_mutex(instance->mutex_compiler_registry, "compiler registry"); |
186 | 144 | |
187 | 144 | /* Create hll symbol tables */ |
188 | 144 | instance->hll_syms = MVM_repr_alloc_init(instance->main_thread, instance->boot_types.BOOTHash); |
189 | 144 | |
190 | 144 | /* Set up hll symbol tables mutex. */ |
191 | 144 | init_mutex(instance->mutex_hll_syms, "hll syms"); |
192 | 144 | |
193 | 144 | /* Create callsite intern pool. */ |
194 | 144 | instance->callsite_interns = MVM_calloc(1, sizeof(MVMCallsiteInterns)); |
195 | 144 | init_mutex(instance->mutex_callsite_interns, "callsite interns"); |
196 | 144 | |
197 | 144 | /* There's some callsites we statically use all over the place. Intern |
198 | 144 | * them, so that spesh may end up optimizing more "internal" stuff. */ |
199 | 144 | MVM_callsite_initialize_common(instance->main_thread); |
200 | 144 | |
201 | 144 | /* Multi-cache additions mutex. */ |
202 | 144 | init_mutex(instance->mutex_multi_cache_add, "multi-cache addition"); |
203 | 144 | |
204 | 144 | /* Current instrumentation level starts at 1; used to trigger all frames |
205 | 144 | * to be verified before their first run. */ |
206 | 144 | instance->instrumentation_level = 1; |
207 | 144 | |
208 | 144 | /* Mutex for spesh installations, and check if we've a file we |
209 | 144 | * should log specializations to. */ |
210 | 144 | init_mutex(instance->mutex_spesh_install, "spesh installations"); |
211 | 144 | spesh_log = getenv("MVM_SPESH_LOG"); |
212 | 144 | if (spesh_log && spesh_log[0]) |
213 | 0 | instance->spesh_log_fh |
214 | 0 | = fopen_perhaps_with_pid("MVM_SPESH_LOG", spesh_log, "w"); |
215 | 144 | spesh_disable = getenv("MVM_SPESH_DISABLE"); |
216 | 144 | if (!spesh_disable || !spesh_disable[0]) { |
217 | 144 | instance->spesh_enabled = 1; |
218 | 144 | spesh_inline_disable = getenv("MVM_SPESH_INLINE_DISABLE"); |
219 | 144 | if (!spesh_inline_disable || !spesh_inline_disable[0]) |
220 | 144 | instance->spesh_inline_enabled = 1; |
221 | 144 | spesh_osr_disable = getenv("MVM_SPESH_OSR_DISABLE"); |
222 | 144 | if (!spesh_osr_disable || !spesh_osr_disable[0]) |
223 | 144 | instance->spesh_osr_enabled = 1; |
224 | 144 | } |
225 | 144 | |
226 | 144 | init_mutex(instance->mutex_parameterization_add, "parameterization"); |
227 | 144 | |
228 | 144 | /* Should we specialize without warm up delays? Used to find bugs in the |
229 | 144 | * specializer and JIT. */ |
230 | 144 | spesh_nodelay = getenv("MVM_SPESH_NODELAY"); |
231 | 144 | if (spesh_nodelay && spesh_nodelay[0]) { |
232 | 0 | instance->spesh_nodelay = 1; |
233 | 0 | } |
234 | 144 | |
235 | 144 | /* Should we limit the number of specialized frames produced? (This is |
236 | 144 | * mostly useful for building spesh bug bisect tools.) */ |
237 | 144 | spesh_limit = getenv("MVM_SPESH_LIMIT"); |
238 | 144 | if (spesh_limit && spesh_limit[0]) |
239 | 0 | instance->spesh_limit = atoi(spesh_limit); |
240 | 144 | |
241 | 144 | /* Should we enforce that a thread, when sending work to the specialzation |
242 | 144 | * worker, block until the specialization worker is done? This is useful |
243 | 144 | * for getting more predictable behavior when debugging. */ |
244 | 144 | spesh_blocking = getenv("MVM_SPESH_BLOCKING"); |
245 | 144 | if (spesh_blocking && spesh_blocking[0]) |
246 | 0 | instance->spesh_blocking = 1; |
247 | 144 | |
248 | 144 | /* JIT environment/logging setup. */ |
249 | 144 | jit_disable = getenv("MVM_JIT_DISABLE"); |
250 | 144 | if (!jit_disable || !jit_disable[0]) |
251 | 144 | instance->jit_enabled = 1; |
252 | 144 | |
253 | 144 | jit_expr_disable = getenv("MVM_JIT_EXPR_DISABLE"); |
254 | 144 | if (!jit_expr_disable || strlen(jit_expr_disable) == 0) |
255 | 144 | instance->jit_expr_enabled = 1; |
256 | 144 | |
257 | 144 | |
258 | 144 | jit_log = getenv("MVM_JIT_LOG"); |
259 | 144 | if (jit_log && jit_log[0]) |
260 | 0 | instance->jit_log_fh = fopen_perhaps_with_pid("MVM_JIT_LOG", jit_log, "w"); |
261 | 144 | jit_bytecode_dir = getenv("MVM_JIT_BYTECODE_DIR"); |
262 | 144 | if (jit_bytecode_dir && jit_bytecode_dir[0]) { |
263 | 0 | size_t bytecode_map_name_size = strlen(jit_bytecode_dir) + strlen("/jit-map.txt") + 1; |
264 | 0 | char *bytecode_map_name = MVM_malloc(bytecode_map_name_size); |
265 | 0 | snprintf(bytecode_map_name, bytecode_map_name_size, "%s/jit-map.txt", jit_bytecode_dir); |
266 | 0 | instance->jit_bytecode_map = fopen(bytecode_map_name, "w"); |
267 | 0 | instance->jit_bytecode_dir = jit_bytecode_dir; |
268 | 0 | MVM_free(bytecode_map_name); |
269 | 0 | } |
270 | 144 | jit_last_frame = getenv("MVM_JIT_EXPR_LAST_FRAME"); |
271 | 144 | jit_last_bb = getenv("MVM_JIT_EXPR_LAST_BB"); |
272 | 144 | |
273 | 144 | /* what could possibly go wrong in integer formats? */ |
274 | 144 | instance->jit_expr_last_frame = jit_last_frame != NULL ? atoi(jit_last_frame) : -1; |
275 | 144 | instance->jit_expr_last_bb = jit_last_bb != NULL ? atoi(jit_last_bb) : -1; |
276 | 144 | instance->jit_seq_nr = 0; |
277 | 144 | |
278 | 144 | /* add JIT debugging breakpoints */ |
279 | 144 | { |
280 | 144 | char *jit_breakpoints_str = getenv("MVM_JIT_BREAKPOINTS"); |
281 | 144 | if (jit_breakpoints_str != NULL) { |
282 | 0 | MVM_VECTOR_INIT(instance->jit_breakpoints, 4); |
283 | 144 | } else { |
284 | 144 | instance->jit_breakpoints_num = 0; |
285 | 144 | instance->jit_breakpoints = NULL; |
286 | 144 | } |
287 | 144 | while (jit_breakpoints_str != NULL && *jit_breakpoints_str) { |
288 | 0 | MVMint32 frame_nr, block_nr, nchars; |
289 | 0 | MVMint32 result = sscanf(jit_breakpoints_str, "%d/%d%n", |
290 | 0 | &frame_nr, &block_nr, &nchars); |
291 | 0 | if (result < 2) |
292 | 0 | break; |
293 | 0 |
|
294 | 0 | MVM_VECTOR_ENSURE_SPACE(instance->jit_breakpoints, 1); |
295 | 0 | instance->jit_breakpoints[instance->jit_breakpoints_num].frame_nr = frame_nr; |
296 | 0 | instance->jit_breakpoints[instance->jit_breakpoints_num].block_nr = block_nr; |
297 | 0 | instance->jit_breakpoints_num++; |
298 | 0 |
|
299 | 0 | jit_breakpoints_str += nchars; |
300 | 0 | if (*jit_breakpoints_str == ':') { |
301 | 0 | jit_breakpoints_str++; |
302 | 0 | } |
303 | 0 | } |
304 | 144 | } |
305 | 144 | |
306 | 144 | /* Spesh thread syncing. */ |
307 | 144 | init_mutex(instance->mutex_spesh_sync, "spesh sync"); |
308 | 144 | init_cond(instance->cond_spesh_sync, "spesh sync"); |
309 | 144 | |
310 | 144 | /* Various kinds of debugging that can be enabled. */ |
311 | 144 | dynvar_log = getenv("MVM_DYNVAR_LOG"); |
312 | 144 | if (dynvar_log && dynvar_log[0]) { |
313 | 0 | instance->dynvar_log_fh = fopen_perhaps_with_pid("MVM_DYNVAR_LOG", dynvar_log, "w"); |
314 | 0 | fprintf(instance->dynvar_log_fh, "+ x 0 0 0 0 0 %"PRIu64"\n", uv_hrtime()); |
315 | 0 | fflush(instance->dynvar_log_fh); |
316 | 0 | instance->dynvar_log_lasttime = uv_hrtime(); |
317 | 0 | } |
318 | 144 | else |
319 | 144 | instance->dynvar_log_fh = NULL; |
320 | 144 | instance->nfa_debug_enabled = getenv("MVM_NFA_DEB") ? 1 : 0; |
321 | 144 | if (getenv("MVM_CROSS_THREAD_WRITE_LOG")) { |
322 | 0 | instance->cross_thread_write_logging = 1; |
323 | 0 | instance->cross_thread_write_logging_include_locked = |
324 | 0 | getenv("MVM_CROSS_THREAD_WRITE_LOG_INCLUDE_LOCKED") ? 1 : 0; |
325 | 0 | instance->instrumentation_level++; |
326 | 0 | init_mutex(instance->mutex_cross_thread_write_logging, |
327 | 0 | "cross thread write logging output"); |
328 | 0 | } |
329 | 144 | else { |
330 | 144 | instance->cross_thread_write_logging = 0; |
331 | 144 | } |
332 | 144 | |
333 | 144 | if (getenv("MVM_COVERAGE_LOG")) { |
334 | 0 | char *coverage_log = getenv("MVM_COVERAGE_LOG"); |
335 | 0 | instance->coverage_logging = 1; |
336 | 0 | instance->instrumentation_level++; |
337 | 0 | if (coverage_log[0]) |
338 | 0 | instance->coverage_log_fh = fopen_perhaps_with_pid("MVM_COVERAGE_LOG", coverage_log, "a"); |
339 | 0 | else |
340 | 0 | instance->coverage_log_fh = stderr; |
341 | 0 |
|
342 | 0 | instance->coverage_control = 0; |
343 | 0 | if (getenv("MVM_COVERAGE_CONTROL")) { |
344 | 0 | char *coverage_control = getenv("MVM_COVERAGE_CONTROL"); |
345 | 0 | if (coverage_control && coverage_control[0]) |
346 | 0 | instance->coverage_control = atoi(coverage_control); |
347 | 0 | } |
348 | 0 | } |
349 | 144 | else { |
350 | 144 | instance->coverage_logging = 0; |
351 | 144 | } |
352 | 144 | |
353 | 144 | /* Create std[in/out/err]. */ |
354 | 144 | setup_std_handles(instance->main_thread); |
355 | 144 | |
356 | 144 | /* Set up the specialization worker thread and a log for the main thread. */ |
357 | 144 | MVM_spesh_worker_setup(instance->main_thread); |
358 | 144 | MVM_spesh_log_initialize_thread(instance->main_thread, 1); |
359 | 144 | |
360 | 144 | /* Back to nursery allocation, now we're set up. */ |
361 | 144 | MVM_gc_allocate_gen2_default_clear(instance->main_thread); |
362 | 144 | |
363 | 144 | return instance; |
364 | 144 | } |
365 | | |
366 | | /* Set up some standard file handles. */ |
367 | 144 | static void setup_std_handles(MVMThreadContext *tc) { |
368 | 144 | tc->instance->stdin_handle = MVM_file_get_stdstream(tc, 0); |
369 | 144 | MVM_gc_root_add_permanent_desc(tc, (MVMCollectable **)&tc->instance->stdin_handle, |
370 | 144 | "stdin handle"); |
371 | 144 | |
372 | 144 | tc->instance->stdout_handle = MVM_file_get_stdstream(tc, 1); |
373 | 144 | MVM_gc_root_add_permanent_desc(tc, (MVMCollectable **)&tc->instance->stdout_handle, |
374 | 144 | "stdout handle"); |
375 | 144 | |
376 | 144 | tc->instance->stderr_handle = MVM_file_get_stdstream(tc, 2); |
377 | 144 | MVM_gc_root_add_permanent_desc(tc, (MVMCollectable **)&tc->instance->stderr_handle, |
378 | 144 | "stderr handle"); |
379 | 144 | } |
380 | | |
381 | | /* This callback is passed to the interpreter code. It takes care of making |
382 | | * the initial invocation. */ |
383 | 288 | static void toplevel_initial_invoke(MVMThreadContext *tc, void *data) { |
384 | 288 | /* Create initial frame, which sets up all of the interpreter state also. */ |
385 | 288 | MVM_frame_invoke(tc, (MVMStaticFrame *)data, MVM_callsite_get_common(tc, MVM_CALLSITE_ID_NULL_ARGS), NULL, NULL, NULL, -1); |
386 | 288 | } |
387 | | |
388 | | /* Loads bytecode from the specified file name and runs it. */ |
389 | 144 | void MVM_vm_run_file(MVMInstance *instance, const char *filename) { |
390 | 144 | /* Map the compilation unit into memory and dissect it. */ |
391 | 144 | MVMThreadContext *tc = instance->main_thread; |
392 | 144 | MVMCompUnit *cu = MVM_cu_map_from_file(tc, filename); |
393 | 144 | |
394 | 144 | MVMROOT(tc, cu, { |
395 | 144 | /* The call to MVM_string_utf8_decode() may allocate, invalidating the |
396 | 144 | location cu->body.filename */ |
397 | 144 | MVMString *const str = MVM_string_utf8_c8_decode(tc, instance->VMString, filename, strlen(filename)); |
398 | 144 | cu->body.filename = str; |
399 | 144 | |
400 | 144 | /* Run deserialization frame, if there is one. Disable specialization |
401 | 144 | * during this time, so we don't waste time logging one-shot setup |
402 | 144 | * code. */ |
403 | 144 | if (cu->body.deserialize_frame) { |
404 | 144 | MVMint8 spesh_enabled_orig = tc->instance->spesh_enabled; |
405 | 144 | tc->instance->spesh_enabled = 0; |
406 | 144 | MVM_interp_run(tc, toplevel_initial_invoke, cu->body.deserialize_frame); |
407 | 144 | tc->instance->spesh_enabled = spesh_enabled_orig; |
408 | 144 | } |
409 | 144 | }); |
410 | 144 | |
411 | 144 | /* Run the entry-point frame. */ |
412 | 144 | MVM_interp_run(tc, toplevel_initial_invoke, cu->body.main_frame); |
413 | 144 | } |
414 | | |
415 | | /* Loads bytecode from the specified file name and dumps it. */ |
416 | 0 | void MVM_vm_dump_file(MVMInstance *instance, const char *filename) { |
417 | 0 | /* Map the compilation unit into memory and dissect it. */ |
418 | 0 | MVMThreadContext *tc = instance->main_thread; |
419 | 0 | MVMCompUnit *cu = MVM_cu_map_from_file(tc, filename); |
420 | 0 | char *dump = MVM_bytecode_dump(tc, cu); |
421 | 0 | size_t dumplen = strlen(dump); |
422 | 0 | int position = 0; |
423 | 0 |
|
424 | 0 | /* libuv already set up stdout to be nonblocking, but it can very well be |
425 | 0 | * we encounter EAGAIN (Resource temporarily unavailable), so we need to |
426 | 0 | * loop over our buffer, which can be quite big. |
427 | 0 | * |
428 | 0 | * The CORE.setting.moarvm has - as of writing this - about 32 megs of |
429 | 0 | * output from dumping. |
430 | 0 | */ |
431 | 0 | while (position < dumplen) { |
432 | 0 | size_t written = write(1, dump + position, dumplen - position); |
433 | 0 | if (written > 0) |
434 | 0 | position += written; |
435 | 0 | } |
436 | 0 |
|
437 | 0 | MVM_free(dump); |
438 | 0 | } |
439 | | |
440 | | /* Exits the process as quickly as is gracefully possible, respecting that |
441 | | * foreground threads should join first. Leaves all cleanup to the OS, as it |
442 | | * will be able to do it much more swiftly than we could. This is typically |
443 | | * not the right thing for embedding; see MVM_vm_destroy_instance for that. */ |
444 | 143 | void MVM_vm_exit(MVMInstance *instance) { |
445 | 143 | /* Join any foreground threads and flush standard handles. */ |
446 | 143 | MVM_thread_join_foreground(instance->main_thread); |
447 | 143 | MVM_io_flush_standard_handles(instance->main_thread); |
448 | 143 | |
449 | 143 | /* Close any spesh or jit log. */ |
450 | 143 | if (instance->spesh_log_fh) |
451 | 0 | fclose(instance->spesh_log_fh); |
452 | 143 | if (instance->jit_log_fh) |
453 | 0 | fclose(instance->jit_log_fh); |
454 | 143 | if (instance->jit_bytecode_map) |
455 | 0 | fclose(instance->jit_bytecode_map); |
456 | 143 | if (instance->dynvar_log_fh) { |
457 | 0 | fprintf(instance->dynvar_log_fh, "- x 0 0 0 0 %"PRId64" %"PRIu64" %"PRIu64"\n", instance->dynvar_log_lasttime, uv_hrtime(), uv_hrtime()); |
458 | 0 | fclose(instance->dynvar_log_fh); |
459 | 0 | } |
460 | 143 | |
461 | 143 | /* And, we're done. */ |
462 | 143 | exit(0); |
463 | 143 | } |
464 | | |
465 | 0 | static void cleanup_callsite_interns(MVMInstance *instance) { |
466 | 0 | int i; |
467 | 0 |
|
468 | 0 | for (i = 0; i < MVM_INTERN_ARITY_LIMIT; i++) { |
469 | 0 | int callsite_count = instance->callsite_interns->num_by_arity[i]; |
470 | 0 | int j; |
471 | 0 |
|
472 | 0 | if (callsite_count) { |
473 | 0 | MVMCallsite **callsites = instance->callsite_interns->by_arity[i]; |
474 | 0 |
|
475 | 0 | for (j = 0; j < callsite_count; j++) { |
476 | 0 | MVMCallsite *callsite = callsites[j]; |
477 | 0 |
|
478 | 0 | if (MVM_callsite_is_common(callsite)) { |
479 | 0 | continue; |
480 | 0 | } |
481 | 0 |
|
482 | 0 | MVM_callsite_destroy(callsite); |
483 | 0 | } |
484 | 0 |
|
485 | 0 | MVM_free(callsites); |
486 | 0 | } |
487 | 0 | } |
488 | 0 | MVM_free(instance->callsite_interns); |
489 | 0 | } |
490 | | |
491 | | /* Destroys a VM instance. This must be called only from the main thread. It |
492 | | * should clear up all resources and free all memory; in practice, it falls |
493 | | * short of this goal at the moment. */ |
494 | 0 | void MVM_vm_destroy_instance(MVMInstance *instance) { |
495 | 0 | /* Join any foreground threads and flush standard handles. */ |
496 | 0 | MVM_thread_join_foreground(instance->main_thread); |
497 | 0 | MVM_io_flush_standard_handles(instance->main_thread); |
498 | 0 |
|
499 | 0 | /* Run the GC global destruction phase. After this, |
500 | 0 | * no 6model object pointers should be accessed. */ |
501 | 0 | MVM_gc_global_destruction(instance->main_thread); |
502 | 0 |
|
503 | 0 | /* Cleanup REPR registry */ |
504 | 0 | uv_mutex_destroy(&instance->mutex_repr_registry); |
505 | 0 | MVM_HASH_DESTROY(instance->main_thread, hash_handle, MVMReprRegistry, instance->repr_hash); |
506 | 0 | MVM_free(instance->repr_list); |
507 | 0 |
|
508 | 0 | /* Clean up GC related resources. */ |
509 | 0 | uv_mutex_destroy(&instance->mutex_permroots); |
510 | 0 | MVM_free(instance->permroots); |
511 | 0 | MVM_free(instance->permroot_descriptions); |
512 | 0 | uv_cond_destroy(&instance->cond_gc_start); |
513 | 0 | uv_cond_destroy(&instance->cond_gc_finish); |
514 | 0 | uv_cond_destroy(&instance->cond_gc_intrays_clearing); |
515 | 0 | uv_cond_destroy(&instance->cond_blocked_can_continue); |
516 | 0 | uv_mutex_destroy(&instance->mutex_gc_orchestrate); |
517 | 0 |
|
518 | 0 | /* Clean up Hash of HLLConfig. */ |
519 | 0 | uv_mutex_destroy(&instance->mutex_hllconfigs); |
520 | 0 | MVM_HASH_DESTROY(instance->main_thread, hash_handle, MVMHLLConfig, instance->compiler_hll_configs); |
521 | 0 | MVM_HASH_DESTROY(instance->main_thread, hash_handle, MVMHLLConfig, instance->compilee_hll_configs); |
522 | 0 |
|
523 | 0 | /* Clean up Hash of DLLs. */ |
524 | 0 | uv_mutex_destroy(&instance->mutex_dll_registry); |
525 | 0 | MVM_HASH_DESTROY(instance->main_thread, hash_handle, MVMDLLRegistry, instance->dll_registry); |
526 | 0 |
|
527 | 0 | /* Clean up Hash of extensions. */ |
528 | 0 | uv_mutex_destroy(&instance->mutex_ext_registry); |
529 | 0 | MVM_HASH_DESTROY(instance->main_thread, hash_handle, MVMExtRegistry, instance->ext_registry); |
530 | 0 |
|
531 | 0 | /* Clean up Hash of extension ops. */ |
532 | 0 | uv_mutex_destroy(&instance->mutex_extop_registry); |
533 | 0 | MVM_HASH_DESTROY(instance->main_thread, hash_handle, MVMExtOpRegistry, instance->extop_registry); |
534 | 0 |
|
535 | 0 | /* Clean up Hash of all known serialization contexts; all SCs list is in |
536 | 0 | * FSA space and so cleaned up with that. */ |
537 | 0 | uv_mutex_destroy(&instance->mutex_sc_registry); |
538 | 0 | MVM_HASH_DESTROY(instance->main_thread, hash_handle, MVMSerializationContextBody, instance->sc_weakhash); |
539 | 0 |
|
540 | 0 | /* Clean up Hash of filenames of compunits loaded from disk. */ |
541 | 0 | uv_mutex_destroy(&instance->mutex_loaded_compunits); |
542 | 0 | MVM_HASH_DESTROY(instance->main_thread, hash_handle, MVMLoadedCompUnitName, instance->loaded_compunits); |
543 | 0 |
|
544 | 0 | /* Clean up Container registry. */ |
545 | 0 | uv_mutex_destroy(&instance->mutex_container_registry); |
546 | 0 | MVM_HASH_DESTROY(instance->main_thread, hash_handle, MVMContainerRegistry, instance->container_registry); |
547 | 0 |
|
548 | 0 | /* Clean up Hash of compiler objects keyed by name. */ |
549 | 0 | uv_mutex_destroy(&instance->mutex_compiler_registry); |
550 | 0 |
|
551 | 0 | /* Clean up Hash of hashes of symbol tables per hll. */ |
552 | 0 | uv_mutex_destroy(&instance->mutex_hll_syms); |
553 | 0 |
|
554 | 0 | /* Clean up multi cache addition mutex. */ |
555 | 0 | uv_mutex_destroy(&instance->mutex_multi_cache_add); |
556 | 0 |
|
557 | 0 | /* Clean up parameterization addition mutex. */ |
558 | 0 | uv_mutex_destroy(&instance->mutex_parameterization_add); |
559 | 0 |
|
560 | 0 | /* Clean up interned callsites */ |
561 | 0 | uv_mutex_destroy(&instance->mutex_callsite_interns); |
562 | 0 | cleanup_callsite_interns(instance); |
563 | 0 |
|
564 | 0 | /* Release this interpreter's hold on Unicode database */ |
565 | 0 | MVM_unicode_release(instance->main_thread); |
566 | 0 |
|
567 | 0 | /* Clean up spesh mutexes and close any log. */ |
568 | 0 | uv_mutex_destroy(&instance->mutex_spesh_install); |
569 | 0 | uv_cond_destroy(&instance->cond_spesh_sync); |
570 | 0 | uv_mutex_destroy(&instance->mutex_spesh_sync); |
571 | 0 | if (instance->spesh_log_fh) |
572 | 0 | fclose(instance->spesh_log_fh); |
573 | 0 | if (instance->jit_log_fh) |
574 | 0 | fclose(instance->jit_log_fh); |
575 | 0 | if (instance->dynvar_log_fh) |
576 | 0 | fclose(instance->dynvar_log_fh); |
577 | 0 | if (instance->jit_breakpoints) { |
578 | 0 | MVM_VECTOR_DESTROY(instance->jit_breakpoints); |
579 | 0 | } |
580 | 0 |
|
581 | 0 |
|
582 | 0 | /* Clean up cross-thread-write-logging mutex */ |
583 | 0 | uv_mutex_destroy(&instance->mutex_cross_thread_write_logging); |
584 | 0 |
|
585 | 0 | /* Clean up NFG. */ |
586 | 0 | uv_mutex_destroy(&instance->nfg->update_mutex); |
587 | 0 | MVM_nfg_destroy(instance->main_thread); |
588 | 0 |
|
589 | 0 | /* Clean up fixed size allocator */ |
590 | 0 | MVM_fixed_size_destroy(instance->fsa); |
591 | 0 |
|
592 | 0 | /* Clean up integer constant and string cache. */ |
593 | 0 | uv_mutex_destroy(&instance->mutex_int_const_cache); |
594 | 0 | MVM_free(instance->int_const_cache); |
595 | 0 | MVM_free(instance->int_to_str_cache); |
596 | 0 |
|
597 | 0 | /* Clean up event loop starting mutex. */ |
598 | 0 | uv_mutex_destroy(&instance->mutex_event_loop_start); |
599 | 0 |
|
600 | 0 | /* Destroy main thread contexts and thread list mutex. */ |
601 | 0 | MVM_tc_destroy(instance->main_thread); |
602 | 0 | uv_mutex_destroy(&instance->mutex_threads); |
603 | 0 |
|
604 | 0 | /* Clear up VM instance memory. */ |
605 | 0 | MVM_free(instance); |
606 | 0 | } |
607 | | |
608 | 144 | void MVM_vm_set_clargs(MVMInstance *instance, int argc, char **argv) { |
609 | 144 | instance->num_clargs = argc; |
610 | 144 | instance->raw_clargs = argv; |
611 | 144 | } |
612 | | |
613 | 144 | void MVM_vm_set_exec_name(MVMInstance *instance, const char *exec_name) { |
614 | 144 | instance->exec_name = exec_name; |
615 | 144 | } |
616 | | |
617 | 144 | void MVM_vm_set_prog_name(MVMInstance *instance, const char *prog_name) { |
618 | 144 | instance->prog_name = prog_name; |
619 | 144 | } |
620 | | |
621 | 144 | void MVM_vm_set_lib_path(MVMInstance *instance, int count, const char **lib_path) { |
622 | 144 | enum { MAX_COUNT = sizeof instance->lib_path / sizeof *instance->lib_path }; |
623 | 144 | |
624 | 144 | int i = 0; |
625 | 144 | |
626 | 144 | if (count > MAX_COUNT) |
627 | 0 | MVM_panic(1, "Cannot set more than %i library paths", MAX_COUNT); |
628 | 144 | |
629 | 144 | for (; i < count; ++i) |
630 | 0 | instance->lib_path[i] = lib_path[i]; |
631 | 144 | |
632 | 144 | /* Clear remainder to allow repeated calls */ |
633 | 1.29k | for (; i < MAX_COUNT; ++i) |
634 | 1.15k | instance->lib_path[i] = NULL; |
635 | 144 | } |