Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license
2 : :
3 : : /*
4 : : task.c
5 : : lightweight processes (symmetric coroutines)
6 : : */
7 : :
8 : : // need this to get the real definition of ucontext_t,
9 : : // if we're going to use the ucontext_t implementation there
10 : : //#if defined(__APPLE__) && defined(JL_HAVE_UCONTEXT)
11 : : //#pragma push_macro("_XOPEN_SOURCE")
12 : : //#define _XOPEN_SOURCE
13 : : //#include <ucontext.h>
14 : : //#pragma pop_macro("_XOPEN_SOURCE")
15 : : //#endif
16 : :
17 : : // this is needed for !COPY_STACKS to work on linux
18 : : #ifdef _FORTIFY_SOURCE
19 : : // disable __longjmp_chk validation so that we can jump between stacks
20 : : // (which would normally be invalid to do with setjmp / longjmp)
21 : : #pragma push_macro("_FORTIFY_SOURCE")
22 : : #undef _FORTIFY_SOURCE
23 : : #include <setjmp.h>
24 : : #pragma pop_macro("_FORTIFY_SOURCE")
25 : : #endif
26 : :
27 : : #include "platform.h"
28 : :
29 : : #include <stdlib.h>
30 : : #include <string.h>
31 : : #include <signal.h>
32 : : #include <unistd.h>
33 : : #include <errno.h>
34 : : #include <inttypes.h>
35 : : #include "julia.h"
36 : : #include "julia_internal.h"
37 : : #include "threading.h"
38 : : #include "julia_assert.h"
39 : :
40 : : #ifdef __cplusplus
41 : : extern "C" {
42 : : #endif
43 : :
44 : : #if defined(_COMPILER_ASAN_ENABLED_)
45 : : static inline void sanitizer_start_switch_fiber(const void* bottom, size_t size) {
46 : : __sanitizer_start_switch_fiber(NULL, bottom, size);
47 : : }
48 : : static inline void sanitizer_finish_switch_fiber(void) {
49 : : __sanitizer_finish_switch_fiber(NULL, NULL, NULL);
50 : : }
51 : : #else
52 : 2545260 : static inline void sanitizer_start_switch_fiber(const void* bottom, size_t size) {}
53 : 2544840 : static inline void sanitizer_finish_switch_fiber(void) {}
54 : : #endif
55 : :
56 : : #if defined(_COMPILER_TSAN_ENABLED_)
57 : : // must defined as macros, since the function containing them must not return before the longjmp
58 : : #define tsan_destroy_ctx(_ptls, _ctx) do { \
59 : : jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
60 : : if (_tsan_macro_ctx != &(_ptls)->root_task->ctx) { \
61 : : __tsan_destroy_fiber(_tsan_macro_ctx->tsan_state); \
62 : : } \
63 : : _tsan_macro_ctx->tsan_state = NULL; \
64 : : } while (0)
65 : : #define tsan_switch_to_ctx(_ctx) do { \
66 : : jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
67 : : __tsan_switch_to_fiber(_tsan_macro_ctx->tsan_state, 0); \
68 : : } while (0)
69 : : #ifdef COPY_STACKS
70 : : #define tsan_destroy_copyctx(_ptls, _ctx) do { \
71 : : jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
72 : : if (_tsan_macro_ctx != &(_ptls)->root_task->ctx) { \
73 : : __tsan_destroy_fiber(_tsan_macro_ctx->tsan_state); \
74 : : } \
75 : : _tsan_macro_ctx->tsan_state = NULL; \
76 : : } while (0)
77 : : #define tsan_switch_to_copyctx(_ctx) do { \
78 : : struct jl_stack_context_t *_tsan_macro_ctx = (_ctx); \
79 : : __tsan_switch_to_fiber(_tsan_macro_ctx->tsan_state, 0); \
80 : : } while (0)
81 : : #endif
82 : : #else
83 : : // just do minimal type-checking on the arguments
84 : : #define tsan_destroy_ctx(_ptls, _ctx) do { \
85 : : jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
86 : : (void)_tsan_macro_ctx; \
87 : : } while (0)
88 : : #define tsan_switch_to_ctx(_ctx) do { \
89 : : jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
90 : : (void)_tsan_macro_ctx; \
91 : : } while (0)
92 : : #ifdef COPY_STACKS
93 : : #define tsan_destroy_copyctx(_ptls, _ctx) do { \
94 : : jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
95 : : (void)_tsan_macro_ctx; \
96 : : } while (0)
97 : : #define tsan_switch_to_copyctx(_ctx) do { \
98 : : jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
99 : : (void)_tsan_macro_ctx; \
100 : : } while (0)
101 : : #endif
102 : : #endif
103 : :
104 : : // empirically, jl_finish_task needs about 64k stack space to infer/run
105 : : // and additionally, gc-stack reserves 64k for the guard pages
106 : : #if defined(MINSIGSTKSZ)
107 : : #define MINSTKSZ (MINSIGSTKSZ > 131072 ? MINSIGSTKSZ : 131072)
108 : : #else
109 : : #define MINSTKSZ 131072
110 : : #endif
111 : :
112 : : #define ROOT_TASK_STACK_ADJUSTMENT 3000000
113 : :
114 : : #ifdef JL_HAVE_ASYNCIFY
115 : : // Switching logic is implemented in JavaScript
116 : : #define STATIC_OR_JS JL_DLLEXPORT
117 : : #else
118 : : #define STATIC_OR_JS static
119 : : #endif
120 : :
121 : : static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) JL_NOTSAFEPOINT;
122 : : STATIC_OR_JS void jl_set_fiber(jl_ucontext_t *t);
123 : : STATIC_OR_JS void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t);
124 : : STATIC_OR_JS void jl_start_fiber_swap(jl_ucontext_t *savet, jl_ucontext_t *t);
125 : : STATIC_OR_JS void jl_start_fiber_set(jl_ucontext_t *t);
126 : :
127 : : #ifdef ALWAYS_COPY_STACKS
128 : : # ifndef COPY_STACKS
129 : : # error "ALWAYS_COPY_STACKS requires COPY_STACKS"
130 : : # endif
131 : : static int always_copy_stacks = 1;
132 : : #else
133 : : static int always_copy_stacks = 0;
134 : : #endif
135 : :
136 : : #ifdef COPY_STACKS
137 : 319956 : static void memcpy_a16(uint64_t *to, uint64_t *from, size_t nb)
138 : : {
139 : 319956 : memcpy((char*)jl_assume_aligned(to, 16), (char*)jl_assume_aligned(from, 16), nb);
140 : : //uint64_t *end = (uint64_t*)((char*)from + nb);
141 : : //while (from < end)
142 : : // *(to++) = *(from++);
143 : 319956 : }
144 : :
145 : 159978 : static void NOINLINE save_stack(jl_ptls_t ptls, jl_task_t *lastt, jl_task_t **pt)
146 : : {
147 : 159978 : char *frame_addr = (char*)((uintptr_t)jl_get_frame_addr() & ~15);
148 : 159978 : char *stackbase = (char*)ptls->stackbase;
149 [ - + ]: 159978 : assert(stackbase > frame_addr);
150 : 159978 : size_t nb = stackbase - frame_addr;
151 : : void *buf;
152 [ + + ]: 159978 : if (lastt->bufsz < nb) {
153 : 114739 : buf = (void*)jl_gc_alloc_buf(ptls, nb);
154 : 114739 : lastt->stkbuf = buf;
155 : 114739 : lastt->bufsz = nb;
156 : : }
157 : : else {
158 : 45239 : buf = lastt->stkbuf;
159 : : }
160 : 159978 : *pt = NULL; // clear the gc-root for the target task before copying the stack for saving
161 : 159978 : lastt->copy_stack = nb;
162 : 159978 : lastt->sticky = 1;
163 : 159978 : memcpy_a16((uint64_t*)buf, (uint64_t*)frame_addr, nb);
164 : : // this task's stack could have been modified after
165 : : // it was marked by an incremental collection
166 : : // move the barrier back instead of walking it again here
167 : 159978 : jl_gc_wb_back(lastt);
168 : 159978 : }
169 : :
170 : 271080 : static void NOINLINE JL_NORETURN restore_stack(jl_task_t *t, jl_ptls_t ptls, char *p)
171 : : {
172 : 271080 : size_t nb = t->copy_stack;
173 : 271080 : char *_x = (char*)ptls->stackbase - nb;
174 [ + + ]: 271080 : if (!p) {
175 : : // switch to a stackframe that's beyond the bounds of the last switch
176 : 128840 : p = _x;
177 [ + + ]: 128840 : if ((char*)&_x > _x) {
178 : 93299 : p = (char*)alloca((char*)&_x - _x);
179 : : }
180 : 128840 : restore_stack(t, ptls, p); // pass p to ensure the compiler can't tailcall this or avoid the alloca
181 : : }
182 : 142240 : void *_y = t->stkbuf;
183 [ + - + - ]: 142240 : assert(_x != NULL && _y != NULL);
184 : 142240 : memcpy_a16((uint64_t*)_x, (uint64_t*)_y, nb); // destroys all but the current stackframe
185 : :
186 : 142240 : sanitizer_start_switch_fiber(t->stkbuf, t->bufsz);
187 : : #if defined(_OS_WINDOWS_)
188 : : jl_setcontext(&t->ctx.copy_ctx);
189 : : #else
190 : 142240 : jl_longjmp(t->ctx.copy_ctx.uc_mcontext, 1);
191 : : #endif
192 : : abort(); // unreachable
193 : : }
194 : :
195 : 17738 : static void restore_stack2(jl_task_t *t, jl_ptls_t ptls, jl_task_t *lastt)
196 : : {
197 [ + - + - ]: 17738 : assert(t->copy_stack && !lastt->copy_stack);
198 : 17738 : size_t nb = t->copy_stack;
199 : 17738 : char *_x = (char*)ptls->stackbase - nb;
200 : 17738 : void *_y = t->stkbuf;
201 [ + - + - ]: 17738 : assert(_x != NULL && _y != NULL);
202 : 17738 : memcpy_a16((uint64_t*)_x, (uint64_t*)_y, nb); // destroys all but the current stackframe
203 : : #if defined(JL_HAVE_UNW_CONTEXT)
204 : : volatile int returns = 0;
205 : : int r = unw_getcontext(&lastt->ctx.ctx);
206 : : if (++returns == 2) // r is garbage after the first return
207 : : return;
208 : : if (r != 0 || returns != 1)
209 : : abort();
210 : : #elif defined(JL_HAVE_ASM) || defined(JL_HAVE_SIGALTSTACK) || defined(_OS_WINDOWS_)
211 [ + + ]: 17738 : if (jl_setjmp(lastt->ctx.copy_ctx.uc_mcontext, 0))
212 : 17738 : return;
213 : : #else
214 : : #error COPY_STACKS is incompatible with this platform
215 : : #endif
216 : 17738 : sanitizer_start_switch_fiber(t->stkbuf, t->bufsz);
217 : 17738 : tsan_switch_to_copyctx(&t->ctx);
218 : : #if defined(_OS_WINDOWS_)
219 : : jl_setcontext(&t->ctx.copy_ctx);
220 : : #else
221 : 17738 : jl_longjmp(t->ctx.copy_ctx.uc_mcontext, 1);
222 : : #endif
223 : : }
224 : : #endif
225 : :
226 : : /* Rooted by the base module */
227 : : static _Atomic(jl_function_t*) task_done_hook_func JL_GLOBALLY_ROOTED = NULL;
228 : :
229 : 1262520 : void JL_NORETURN jl_finish_task(jl_task_t *t)
230 : : {
231 : 1262520 : jl_task_t *ct = jl_current_task;
232 : : JL_PROBE_RT_FINISH_TASK(ct);
233 : 1262520 : JL_SIGATOMIC_BEGIN();
234 [ + + ]: 1262520 : if (jl_atomic_load_relaxed(&t->_isexception))
235 : 393 : jl_atomic_store_release(&t->_state, JL_TASK_STATE_FAILED);
236 : : else
237 : 1262130 : jl_atomic_store_release(&t->_state, JL_TASK_STATE_DONE);
238 [ + + ]: 1262520 : if (t->copy_stack) // early free of stkbuf
239 : 171953 : t->stkbuf = NULL;
240 : : // ensure that state is cleared
241 : 1262520 : ct->ptls->in_finalizer = 0;
242 : 1262520 : ct->ptls->in_pure_callback = 0;
243 : 1262520 : ct->world_age = jl_atomic_load_acquire(&jl_world_counter);
244 : : // let the runtime know this task is dead and find a new task to run
245 : 1262520 : jl_function_t *done = jl_atomic_load_relaxed(&task_done_hook_func);
246 [ + + ]: 1262520 : if (done == NULL) {
247 : 115 : done = (jl_function_t*)jl_get_global(jl_base_module, jl_symbol("task_done_hook"));
248 [ + - ]: 115 : if (done != NULL)
249 : 115 : jl_atomic_store_release(&task_done_hook_func, done);
250 : : }
251 [ + + ]: 1262520 : if (done != NULL) {
252 : 1259620 : jl_value_t *args[2] = {done, (jl_value_t*)t};
253 [ + + + - ]: 1259620 : JL_TRY {
254 : 1257850 : jl_apply(args, 2);
255 : : }
256 [ - - ]: 21 : JL_CATCH {
257 : 0 : jl_no_exc_handler(jl_current_exception());
258 : : }
259 : : }
260 : 2906 : jl_gc_debug_critical_error();
261 : 0 : abort();
262 : : }
263 : :
264 : 0 : JL_DLLEXPORT void *jl_task_stack_buffer(jl_task_t *task, size_t *size, int *ptid)
265 : : {
266 : 0 : size_t off = 0;
267 : : #ifndef _OS_WINDOWS_
268 [ # # ]: 0 : if (jl_all_tls_states[0]->root_task == task) {
269 : : // See jl_init_root_task(). The root task of the main thread
270 : : // has its buffer enlarged by an artificial 3000000 bytes, but
271 : : // that means that the start of the buffer usually points to
272 : : // inaccessible memory. We need to correct for this.
273 : 0 : off = ROOT_TASK_STACK_ADJUSTMENT;
274 : : }
275 : : #endif
276 : 0 : jl_ptls_t ptls2 = task->ptls;
277 : 0 : *ptid = -1;
278 [ # # ]: 0 : if (ptls2) {
279 : 0 : *ptid = jl_atomic_load_relaxed(&task->tid);
280 : : #ifdef COPY_STACKS
281 [ # # ]: 0 : if (task->copy_stack) {
282 : 0 : *size = ptls2->stacksize;
283 : 0 : return (char *)ptls2->stackbase - *size;
284 : : }
285 : : #endif
286 : : }
287 : 0 : *size = task->bufsz - off;
288 : 0 : return (void *)((char *)task->stkbuf + off);
289 : : }
290 : :
291 : 0 : JL_DLLEXPORT void jl_active_task_stack(jl_task_t *task,
292 : : char **active_start, char **active_end,
293 : : char **total_start, char **total_end)
294 : : {
295 [ # # ]: 0 : if (!task->started) {
296 : 0 : *total_start = *active_start = 0;
297 : 0 : *total_end = *active_end = 0;
298 : 0 : return;
299 : : }
300 : :
301 : 0 : jl_ptls_t ptls2 = task->ptls;
302 [ # # # # ]: 0 : if (task->copy_stack && ptls2) {
303 : 0 : *total_start = *active_start = (char*)ptls2->stackbase - ptls2->stacksize;
304 : 0 : *total_end = *active_end = (char*)ptls2->stackbase;
305 : : }
306 [ # # ]: 0 : else if (task->stkbuf) {
307 : 0 : *total_start = *active_start = (char*)task->stkbuf;
308 : : #ifndef _OS_WINDOWS_
309 [ # # ]: 0 : if (jl_all_tls_states[0]->root_task == task) {
310 : : // See jl_init_root_task(). The root task of the main thread
311 : : // has its buffer enlarged by an artificial 3000000 bytes, but
312 : : // that means that the start of the buffer usually points to
313 : : // inaccessible memory. We need to correct for this.
314 : 0 : *active_start += ROOT_TASK_STACK_ADJUSTMENT;
315 : 0 : *total_start += ROOT_TASK_STACK_ADJUSTMENT;
316 : : }
317 : : #endif
318 : :
319 : 0 : *total_end = *active_end = (char*)task->stkbuf + task->bufsz;
320 : : #ifdef COPY_STACKS
321 : : // save_stack stores the stack of an inactive task in stkbuf, and the
322 : : // actual number of used bytes in copy_stack.
323 [ # # ]: 0 : if (task->copy_stack > 1)
324 : 0 : *active_end = (char*)task->stkbuf + task->copy_stack;
325 : : #endif
326 : : }
327 : : else {
328 : : // no stack allocated yet
329 : 0 : *total_start = *active_start = 0;
330 : 0 : *total_end = *active_end = 0;
331 : 0 : return;
332 : : }
333 : :
334 [ # # ]: 0 : if (task == jl_current_task) {
335 : : // scan up to current `sp` for current thread and task
336 : 0 : *active_start = (char*)jl_get_frame_addr();
337 : : }
338 : : }
339 : :
340 : : // Marked noinline so we can consistently skip the associated frame.
341 : : // `skip` is number of additional frames to skip.
342 : 253866 : NOINLINE static void record_backtrace(jl_ptls_t ptls, int skip) JL_NOTSAFEPOINT
343 : : {
344 : : // storing bt_size in ptls ensures roots in bt_data will be found
345 : 253866 : ptls->bt_size = rec_backtrace(ptls->bt_data, JL_MAX_BT_SIZE, skip + 1);
346 : 253867 : }
347 : :
348 : 0 : JL_DLLEXPORT void jl_set_next_task(jl_task_t *task) JL_NOTSAFEPOINT
349 : : {
350 : 0 : jl_current_task->ptls->next_task = task;
351 : 0 : }
352 : :
353 : 7 : JL_DLLEXPORT jl_task_t *jl_get_next_task(void) JL_NOTSAFEPOINT
354 : : {
355 : 7 : jl_task_t *ct = jl_current_task;
356 [ + - ]: 7 : if (ct->ptls->next_task)
357 : 7 : return ct->ptls->next_task;
358 : 0 : return ct;
359 : : }
360 : :
361 : : #ifdef _COMPILER_TSAN_ENABLED_
362 : : const char tsan_state_corruption[] = "TSAN state corrupted. Exiting HARD!\n";
363 : : #endif
364 : :
365 : 2546880 : static void ctx_switch(jl_task_t *lastt)
366 : : {
367 : 2546880 : jl_ptls_t ptls = lastt->ptls;
368 : 2546880 : jl_task_t **pt = &ptls->next_task;
369 : 2546880 : jl_task_t *t = *pt;
370 [ - + ]: 2546880 : assert(t != lastt);
371 : : // none of these locks should be held across a task switch
372 [ - + ]: 2546880 : assert(ptls->locks.len == 0);
373 : :
374 : : #ifdef _COMPILER_TSAN_ENABLED_
375 : : if (lastt->ctx.tsan_state != __tsan_get_current_fiber()) {
376 : : // Something went really wrong - don't even assume that we can
377 : : // use assert/abort which involve lots of signal handling that
378 : : // looks at the tsan state.
379 : : write(STDERR_FILENO, tsan_state_corruption, sizeof(tsan_state_corruption) - 1);
380 : : _exit(1);
381 : : }
382 : : #endif
383 : :
384 : 2546880 : int killed = jl_atomic_load_relaxed(&lastt->_state) != JL_TASK_STATE_RUNNABLE;
385 [ + + + + ]: 2546880 : if (!t->started && !t->copy_stack) {
386 : : // may need to allocate the stack
387 [ + + ]: 1267810 : if (t->stkbuf == NULL) {
388 : 1267810 : t->stkbuf = jl_alloc_fiber(&t->ctx.ctx, &t->bufsz, t);
389 [ + + ]: 1267290 : if (t->stkbuf == NULL) {
390 : : #ifdef COPY_STACKS
391 : : // fall back to stack copying if mmap fails
392 : 171953 : t->copy_stack = 1;
393 : 171953 : t->sticky = 1;
394 : 171953 : t->bufsz = 0;
395 [ - + ]: 171953 : if (always_copy_stacks)
396 : 0 : memcpy(&t->ctx.copy_ctx, &ptls->copy_stack_ctx, sizeof(t->ctx.copy_ctx));
397 : : else
398 : 171953 : memcpy(&t->ctx.ctx, &ptls->base_ctx, sizeof(t->ctx.ctx));
399 : : #else
400 : : jl_throw(jl_memory_exception);
401 : : #endif
402 : : }
403 : : }
404 : : }
405 : :
406 [ + + ]: 2546360 : if (killed) {
407 : 1264870 : *pt = NULL; // can't fail after here: clear the gc-root for the target task now
408 : 1264870 : lastt->gcstack = NULL;
409 [ + + + # ]: 1264870 : if (!lastt->copy_stack && lastt->stkbuf) {
410 : : // early free of stkbuf back to the pool
411 : 1092900 : jl_release_task_stack(ptls, lastt);
412 : : }
413 : : }
414 : : else {
415 : : #ifdef COPY_STACKS
416 [ + + ]: 1281500 : if (lastt->copy_stack) { // save the old copy-stack
417 : 159978 : save_stack(ptls, lastt, pt); // allocates (gc-safepoint, and can also fail)
418 [ + + ]: 159978 : if (jl_setjmp(lastt->ctx.copy_ctx.uc_mcontext, 0)) {
419 : 159978 : sanitizer_finish_switch_fiber();
420 : : // TODO: mutex unlock the thread we just switched from
421 : 159978 : return;
422 : : }
423 : : }
424 : : else
425 : : #endif
426 : 1121520 : *pt = NULL; // can't fail after here: clear the gc-root for the target task now
427 : : }
428 : :
429 : : // set up global state for new task and clear global state for old task
430 : 2545160 : t->ptls = ptls;
431 : 2545160 : jl_atomic_store_relaxed(&ptls->current_task, t);
432 : : JL_GC_PROMISE_ROOTED(t);
433 : 2545160 : jl_signal_fence();
434 : 2545160 : jl_set_pgcstack(&t->gcstack);
435 : 2545330 : jl_signal_fence();
436 : 2545330 : lastt->ptls = NULL;
437 : : #ifdef MIGRATE_TASKS
438 : 2545330 : ptls->previous_task = lastt;
439 : : #endif
440 : :
441 [ + + ]: 2545330 : if (t->started) {
442 : : #ifdef COPY_STACKS
443 [ + + ]: 1283670 : if (t->copy_stack) {
444 [ + + + + ]: 159978 : if (!killed && !lastt->copy_stack)
445 : 17738 : restore_stack2(t, ptls, lastt);
446 : : else {
447 : 142240 : tsan_switch_to_copyctx(&t->ctx);
448 [ + + ]: 142240 : if (killed)
449 : 122427 : tsan_destroy_copyctx(ptls, &lastt->ctx);
450 : :
451 [ + + ]: 142240 : if (lastt->copy_stack) {
452 : 128840 : restore_stack(t, ptls, NULL); // (doesn't return)
453 : : }
454 : : else {
455 : 13400 : restore_stack(t, ptls, (char*)1); // (doesn't return)
456 : : }
457 : : }
458 : : }
459 : : else
460 : : #endif
461 : : {
462 : 1123690 : sanitizer_start_switch_fiber(t->stkbuf, t->bufsz);
463 [ + + ]: 1123660 : if (killed) {
464 : 648317 : tsan_switch_to_ctx(&t->ctx);
465 : 648317 : tsan_destroy_ctx(ptls, &lastt->ctx);
466 : 648317 : jl_set_fiber(&t->ctx); // (doesn't return)
467 : 0 : abort(); // unreachable
468 : : }
469 : : else {
470 [ + + ]: 475348 : if (lastt->copy_stack) {
471 : : // Resume at the jl_setjmp earlier in this function,
472 : : // don't do a full task swap
473 : 8197 : tsan_switch_to_ctx(&t->ctx);
474 : 8197 : jl_set_fiber(&t->ctx); // (doesn't return)
475 : : }
476 : : else {
477 : 467151 : jl_swap_fiber(&lastt->ctx, &t->ctx);
478 : : }
479 : : }
480 : : }
481 : : }
482 : : else {
483 : 1261670 : sanitizer_start_switch_fiber(t->stkbuf, t->bufsz);
484 [ + + - + ]: 1266500 : if (t->copy_stack && always_copy_stacks) {
485 : 0 : tsan_switch_to_ctx(&t->ctx);
486 [ # # ]: 0 : if (killed) {
487 : 0 : tsan_destroy_ctx(ptls, &lastt->ctx);
488 : : }
489 : : #ifdef COPY_STACKS
490 : : #if defined(_OS_WINDOWS_)
491 : : jl_setcontext(&t->ctx.copy_ctx);
492 : : #else
493 : 0 : jl_longjmp(t->ctx.copy_ctx.uc_mcontext, 1);
494 : : #endif
495 : : #endif
496 : : abort(); // unreachable
497 : : }
498 : : else {
499 [ + + ]: 1266500 : if (killed) {
500 : 495947 : tsan_switch_to_ctx(&t->ctx);
501 : 495947 : tsan_destroy_ctx(ptls, &lastt->ctx);
502 : 495947 : jl_start_fiber_set(&t->ctx); // (doesn't return)
503 : 0 : abort();
504 : : }
505 [ + + ]: 770552 : else if (lastt->copy_stack) {
506 : : // Resume at the jl_setjmp earlier in this function
507 : 131968 : tsan_switch_to_ctx(&t->ctx);
508 : 131968 : jl_start_fiber_set(&t->ctx); // (doesn't return)
509 : 0 : abort();
510 : : }
511 : : else {
512 : 638584 : jl_start_fiber_swap(&lastt->ctx, &t->ctx);
513 : : }
514 : : }
515 : : }
516 : 1124530 : sanitizer_finish_switch_fiber();
517 : : }
518 : :
519 : 2746000 : JL_DLLEXPORT void jl_switch(void)
520 : : {
521 : 2746000 : jl_task_t *ct = jl_current_task;
522 : 2746000 : jl_ptls_t ptls = ct->ptls;
523 : 2746000 : jl_task_t *t = ptls->next_task;
524 [ + + ]: 2746000 : if (t == ct) {
525 : 197057 : return;
526 : : }
527 [ + + - + ]: 2548940 : if (t->started && t->stkbuf == NULL)
528 : 0 : jl_error("attempt to switch to exited task");
529 [ + + ]: 2548940 : if (ptls->in_finalizer)
530 : 3 : jl_error("task switch not allowed from inside gc finalizer");
531 [ + + ]: 2548940 : if (ptls->in_pure_callback)
532 : 4 : jl_error("task switch not allowed from inside staged nor pure functions");
533 [ - + ]: 2548940 : if (!jl_set_task_tid(t, jl_atomic_load_relaxed(&ct->tid))) // manually yielding to a task
534 : 0 : jl_error("cannot switch to task running on another thread");
535 : :
536 : : JL_PROBE_RT_PAUSE_TASK(ct);
537 : :
538 : : // Store old values on the stack and reset
539 : 2546730 : sig_atomic_t defer_signal = ptls->defer_signal;
540 : 2546730 : int8_t gc_state = jl_gc_unsafe_enter(ptls);
541 : 2546980 : int finalizers_inhibited = ptls->finalizers_inhibited;
542 : 2546980 : ptls->finalizers_inhibited = 0;
543 : :
544 : : #ifdef ENABLE_TIMINGS
545 : : jl_timing_block_t *blk = ptls->timing_stack;
546 : : if (blk)
547 : : jl_timing_block_stop(blk);
548 : : ptls->timing_stack = NULL;
549 : : #endif
550 : :
551 : 2546980 : ctx_switch(ct);
552 : :
553 : : #ifdef MIGRATE_TASKS
554 : 1283810 : ptls = ct->ptls;
555 : 1283810 : t = ptls->previous_task;
556 : 1283810 : ptls->previous_task = NULL;
557 [ - + ]: 1283810 : assert(t != ct);
558 [ - + ]: 1283810 : assert(jl_atomic_load_relaxed(&t->tid) == ptls->tid);
559 [ + + + # ]: 1283810 : if (!t->sticky && !t->copy_stack)
560 : 452125 : jl_atomic_store_release(&t->tid, -1);
561 : : #else
562 : : assert(ptls == ct->ptls);
563 : : #endif
564 : :
565 : : // Pop old values back off the stack
566 [ + + + # : 1283810 : assert(ct == jl_current_task &&
+ # ]
567 : : 0 != ct->ptls &&
568 : : 0 == ptls->finalizers_inhibited);
569 : 1283750 : ptls->finalizers_inhibited = finalizers_inhibited;
570 : :
571 : : #ifdef ENABLE_TIMINGS
572 : : assert(ptls->timing_stack == NULL);
573 : : ptls->timing_stack = blk;
574 : : if (blk)
575 : : jl_timing_block_start(blk);
576 : : #else
577 : : (void)ct;
578 : : #endif
579 : :
580 : 1283750 : jl_gc_unsafe_leave(ptls, gc_state);
581 : 1283740 : sig_atomic_t other_defer_signal = ptls->defer_signal;
582 : 1283740 : ptls->defer_signal = defer_signal;
583 [ + + + + ]: 1283740 : if (other_defer_signal && !defer_signal)
584 : 203 : jl_sigint_safepoint(ptls);
585 : :
586 : : JL_PROBE_RT_RUN_TASK(ct);
587 : : }
588 : :
589 : 0 : JL_DLLEXPORT void jl_switchto(jl_task_t **pt)
590 : : {
591 : 0 : jl_set_next_task(*pt);
592 : 0 : jl_switch();
593 : 0 : }
594 : :
595 : 0 : JL_DLLEXPORT JL_NORETURN void jl_no_exc_handler(jl_value_t *e)
596 : : {
597 : : // NULL exception objects are used when rethrowing. we don't have a handler to process
598 : : // the exception stack, so at least report the exception at the top of the stack.
599 [ # # ]: 0 : if (!e)
600 : 0 : e = jl_current_exception();
601 : :
602 : 0 : jl_printf((JL_STREAM*)STDERR_FILENO, "fatal: error thrown and no exception handler available.\n");
603 : 0 : jl_static_show((JL_STREAM*)STDERR_FILENO, e);
604 : 0 : jl_printf((JL_STREAM*)STDERR_FILENO, "\n");
605 : 0 : jlbacktrace(); // written to STDERR_FILENO
606 : 0 : jl_exit(1);
607 : : }
608 : :
609 : : // yield to exception handler
610 : 290593 : static void JL_NORETURN throw_internal(jl_task_t *ct, jl_value_t *exception JL_MAYBE_UNROOTED)
611 : : {
612 [ - + ]: 290593 : assert(!jl_get_safe_restore());
613 : 290593 : jl_ptls_t ptls = ct->ptls;
614 : 290593 : ptls->io_wait = 0;
615 : 290593 : JL_GC_PUSH1(&exception);
616 : 290593 : jl_gc_unsafe_enter(ptls);
617 [ + + ]: 290593 : if (exception) {
618 : : // The temporary ptls->bt_data is rooted by special purpose code in the
619 : : // GC. This exists only for the purpose of preserving bt_data until we
620 : : // set ptls->bt_size=0 below.
621 : 253871 : jl_push_excstack(&ct->excstack, exception,
622 : 253871 : ptls->bt_data, ptls->bt_size);
623 : 253871 : ptls->bt_size = 0;
624 : : }
625 [ + - + - ]: 290593 : assert(ct->excstack && ct->excstack->top);
626 : 290593 : jl_handler_t *eh = ct->eh;
627 [ + - ]: 290593 : if (eh != NULL) {
628 : : #ifdef ENABLE_TIMINGS
629 : : jl_timing_block_t *cur_block = ptls->timing_stack;
630 : : while (cur_block && eh->timing_stack != cur_block) {
631 : : cur_block = jl_pop_timing_block(cur_block);
632 : : }
633 : : assert(cur_block == eh->timing_stack);
634 : : #endif
635 : 290593 : jl_longjmp(eh->eh_ctx, 1);
636 : : }
637 : : else {
638 : 0 : jl_no_exc_handler(exception);
639 : : }
640 : : assert(0);
641 : : }
642 : :
643 : : // record backtrace and raise an error
644 : 253863 : JL_DLLEXPORT void jl_throw(jl_value_t *e JL_MAYBE_UNROOTED)
645 : : {
646 [ - + ]: 253863 : assert(e != NULL);
647 : 253863 : jl_jmp_buf *safe_restore = jl_get_safe_restore();
648 [ - + ]: 253863 : if (safe_restore)
649 : 0 : jl_longjmp(*safe_restore, 1);
650 : 253863 : jl_task_t *ct = jl_get_current_task();
651 [ - + ]: 253863 : if (ct == NULL) // During startup
652 : 0 : jl_no_exc_handler(e);
653 : 253863 : record_backtrace(ct->ptls, 1);
654 : 253864 : throw_internal(ct, e);
655 : : }
656 : :
657 : : // rethrow with current excstack state
658 : 36644 : JL_DLLEXPORT void jl_rethrow(void)
659 : : {
660 : 36644 : jl_task_t *ct = jl_current_task;
661 : 36644 : jl_excstack_t *excstack = ct->excstack;
662 [ + - + + ]: 36644 : if (!excstack || excstack->top == 0)
663 : 1 : jl_error("rethrow() not allowed outside a catch block");
664 : 36643 : throw_internal(ct, NULL);
665 : : }
666 : :
667 : : // Special case throw for errors detected inside signal handlers. This is not
668 : : // (cannot be) called directly in the signal handler itself, but is returned to
669 : : // after the signal handler exits.
670 : 7 : JL_DLLEXPORT void JL_NORETURN jl_sig_throw(void)
671 : : {
672 : 7 : CFI_NORETURN
673 : 7 : jl_jmp_buf *safe_restore = jl_get_safe_restore();
674 [ - + ]: 7 : if (safe_restore)
675 : 0 : jl_longjmp(*safe_restore, 1);
676 : 7 : jl_task_t *ct = jl_current_task;
677 : 7 : jl_ptls_t ptls = ct->ptls;
678 : 7 : jl_value_t *e = ptls->sig_exception;
679 : 7 : ptls->sig_exception = NULL;
680 : 7 : throw_internal(ct, e);
681 : : }
682 : :
683 : 80 : JL_DLLEXPORT void jl_rethrow_other(jl_value_t *e JL_MAYBE_UNROOTED)
684 : : {
685 : : // TODO: Should uses of `rethrow(exc)` be replaced with a normal throw, now
686 : : // that exception stacks allow root cause analysis?
687 : 80 : jl_task_t *ct = jl_current_task;
688 : 80 : jl_excstack_t *excstack = ct->excstack;
689 [ + - + + ]: 80 : if (!excstack || excstack->top == 0)
690 : 1 : jl_error("rethrow(exc) not allowed outside a catch block");
691 : : // overwrite exception on top of stack. see jl_excstack_exception
692 : 79 : jl_excstack_raw(excstack)[excstack->top-1].jlvalue = e;
693 : : JL_GC_PROMISE_ROOTED(e);
694 : 79 : throw_internal(ct, NULL);
695 : : }
696 : :
697 : : /* This is xoshiro256++ 1.0, used for tasklocal random number generation in Julia.
698 : : This implementation is intended for embedders and internal use by the runtime, and is
699 : : based on the reference implementation at https://prng.di.unimi.it
700 : :
701 : : Credits go to David Blackman and Sebastiano Vigna for coming up with this PRNG.
702 : : They described xoshiro256++ in "Scrambled Linear Pseudorandom Number Generators",
703 : : ACM Trans. Math. Softw., 2021.
704 : :
705 : : There is a pure Julia implementation in stdlib that tends to be faster when used from
706 : : within Julia, due to inlining and more agressive architecture-specific optimizations.
707 : : */
708 : 5080900 : uint64_t jl_genrandom(uint64_t rngState[4]) JL_NOTSAFEPOINT
709 : : {
710 : 5080900 : uint64_t s0 = rngState[0];
711 : 5080900 : uint64_t s1 = rngState[1];
712 : 5080900 : uint64_t s2 = rngState[2];
713 : 5080900 : uint64_t s3 = rngState[3];
714 : :
715 : 5080900 : uint64_t t = s1 << 17;
716 : 5080900 : uint64_t tmp = s0 + s3;
717 : 5080900 : uint64_t res = ((tmp << 23) | (tmp >> 41)) + s0;
718 : 5080900 : s2 ^= s0;
719 : 5080900 : s3 ^= s1;
720 : 5080900 : s1 ^= s2;
721 : 5080900 : s0 ^= s3;
722 : 5080900 : s2 ^= t;
723 : 5080900 : s3 = (s3 << 45) | (s3 >> 19);
724 : :
725 : 5080900 : rngState[0] = s0;
726 : 5080900 : rngState[1] = s1;
727 : 5080900 : rngState[2] = s2;
728 : 5080900 : rngState[3] = s3;
729 : 5080900 : return res;
730 : : }
731 : :
732 : 1272170 : void jl_rng_split(uint64_t to[4], uint64_t from[4]) JL_NOTSAFEPOINT
733 : : {
734 : : /* TODO: consider a less ad-hoc construction
735 : : Ideally we could just use the output of the random stream to seed the initial
736 : : state of the child. Out of an overabundance of caution we multiply with
737 : : effectively random coefficients, to break possible self-interactions.
738 : :
739 : : It is not the goal to mix bits -- we work under the assumption that the
740 : : source is well-seeded, and its output looks effectively random.
741 : : However, xoshiro has never been studied in the mode where we seed the
742 : : initial state with the output of another xoshiro instance.
743 : :
744 : : Constants have nothing up their sleeve:
745 : : 0x02011ce34bce797f == hash(UInt(1))|0x01
746 : : 0x5a94851fb48a6e05 == hash(UInt(2))|0x01
747 : : 0x3688cf5d48899fa7 == hash(UInt(3))|0x01
748 : : 0x867b4bb4c42e5661 == hash(UInt(4))|0x01
749 : : */
750 : 1272170 : to[0] = 0x02011ce34bce797f * jl_genrandom(from);
751 : 1273490 : to[1] = 0x5a94851fb48a6e05 * jl_genrandom(from);
752 : 1273340 : to[2] = 0x3688cf5d48899fa7 * jl_genrandom(from);
753 : 1273110 : to[3] = 0x867b4bb4c42e5661 * jl_genrandom(from);
754 : 1273200 : }
755 : :
756 : 1268440 : JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion_future, size_t ssize)
757 : : {
758 : 1268440 : jl_task_t *ct = jl_current_task;
759 : 1268440 : jl_task_t *t = (jl_task_t*)jl_gc_alloc(ct->ptls, sizeof(jl_task_t), jl_task_type);
760 : : JL_PROBE_RT_NEW_TASK(ct, t);
761 : 1269700 : t->copy_stack = 0;
762 [ + # ]: 1269700 : if (ssize == 0) {
763 : : // stack size unspecified; use default
764 [ - + ]: 1269720 : if (always_copy_stacks) {
765 : 0 : t->copy_stack = 1;
766 : 0 : t->bufsz = 0;
767 : : }
768 : : else {
769 : 1269720 : t->bufsz = JL_STACK_SIZE;
770 : : }
771 : 1269720 : t->stkbuf = NULL;
772 : : }
773 : : else {
774 : : // user requested dedicated stack of a certain size
775 [ - # ]: 0 : if (ssize < MINSTKSZ)
776 : 0 : ssize = MINSTKSZ;
777 : 0 : t->bufsz = ssize;
778 : 0 : t->stkbuf = jl_alloc_fiber(&t->ctx.ctx, &t->bufsz, t);
779 [ # # ]: 0 : if (t->stkbuf == NULL)
780 : 0 : jl_throw(jl_memory_exception);
781 : : }
782 : 1269720 : t->next = jl_nothing;
783 : 1269720 : t->queue = jl_nothing;
784 : 1269720 : t->tls = jl_nothing;
785 : 1269720 : jl_atomic_store_relaxed(&t->_state, JL_TASK_STATE_RUNNABLE);
786 : 1269720 : t->start = start;
787 : 1269720 : t->result = jl_nothing;
788 : 1269720 : t->donenotify = completion_future;
789 : 1269720 : jl_atomic_store_relaxed(&t->_isexception, 0);
790 : : // Inherit logger state from parent task
791 : 1269720 : t->logstate = ct->logstate;
792 : : // Fork task-local random state from parent
793 : 1269720 : jl_rng_split(t->rngState, ct->rngState);
794 : : // there is no active exception handler available on this stack yet
795 : 1270650 : t->eh = NULL;
796 : 1270650 : t->sticky = 1;
797 : 1270650 : t->gcstack = NULL;
798 : 1270650 : t->excstack = NULL;
799 : 1270650 : t->started = 0;
800 : 1270650 : t->priority = 0;
801 [ - + ]: 1270650 : jl_atomic_store_relaxed(&t->tid, t->copy_stack ? jl_atomic_load_relaxed(&ct->tid) : -1); // copy_stacks are always pinned since they can't be moved
802 : 1270650 : t->threadpoolid = ct->threadpoolid;
803 : 1270650 : t->ptls = NULL;
804 : 1270650 : t->world_age = ct->world_age;
805 : :
806 : : #ifdef COPY_STACKS
807 [ + # ]: 1270650 : if (!t->copy_stack) {
808 : : #if defined(JL_DEBUG_BUILD)
809 : 1271010 : memset(&t->ctx, 0, sizeof(t->ctx));
810 : : #endif
811 : : }
812 : : else {
813 [ - # ]: 0 : if (always_copy_stacks)
814 : 0 : memcpy(&t->ctx.copy_ctx, &ct->ptls->copy_stack_ctx, sizeof(t->ctx.copy_ctx));
815 : : else
816 : 0 : memcpy(&t->ctx.ctx, &ct->ptls->base_ctx, sizeof(t->ctx.ctx));
817 : : }
818 : : #endif
819 : : #ifdef _COMPILER_TSAN_ENABLED_
820 : : t->ctx.tsan_state = __tsan_create_fiber(0);
821 : : #endif
822 : 1270650 : return t;
823 : : }
824 : :
825 : : // a version of jl_current_task safe for unmanaged threads
826 : 319515 : JL_DLLEXPORT jl_task_t *jl_get_current_task(void)
827 : : {
828 : 319515 : jl_gcframe_t **pgcstack = jl_get_pgcstack();
829 [ + + ]: 319515 : return pgcstack == NULL ? NULL : container_of(pgcstack, jl_task_t, gcstack);
830 : : }
831 : :
832 : :
833 : : #ifdef JL_HAVE_ASYNCIFY
834 : : JL_DLLEXPORT jl_ucontext_t *task_ctx_ptr(jl_task_t *t)
835 : : {
836 : : return &t->ctx.ctx;
837 : : }
838 : :
839 : : JL_DLLEXPORT jl_value_t *jl_get_root_task(void)
840 : : {
841 : : jl_task_t *ct = jl_current_task;
842 : : return (jl_value_t*)ct->ptls->root_task;
843 : : }
844 : :
845 : : JL_DLLEXPORT void jl_task_wait()
846 : : {
847 : : static jl_function_t *wait_func = NULL;
848 : : if (!wait_func) {
849 : : wait_func = (jl_function_t*)jl_get_global(jl_base_module, jl_symbol("wait"));
850 : : }
851 : : jl_task_t *ct = jl_current_task;
852 : : size_t last_age = ct->world_age;
853 : : ct->world_age = jl_get_world_counter();
854 : : jl_apply(&wait_func, 1);
855 : : ct->world_age = last_age;
856 : : }
857 : :
858 : : JL_DLLEXPORT void jl_schedule_task(jl_task_t *task)
859 : : {
860 : : static jl_function_t *sched_func = NULL;
861 : : if (!sched_func) {
862 : : sched_func = (jl_function_t*)jl_get_global(jl_base_module, jl_symbol("schedule"));
863 : : }
864 : : jl_task_t *ct = jl_current_task;
865 : : size_t last_age = ct->world_age;
866 : : ct->world_age = jl_get_world_counter();
867 : : jl_value_t *args[] = {(jl_value_t*)sched_func, (jl_value_t*)task};
868 : : jl_apply(args, 2);
869 : : ct->world_age = last_age;
870 : : }
871 : : #endif
872 : :
873 : : // Do one-time initializations for task system
874 : 573 : void jl_init_tasks(void) JL_GC_DISABLED
875 : : {
876 : 573 : char *acs = getenv("JULIA_COPY_STACKS");
877 [ - + ]: 573 : if (acs) {
878 [ # # # # ]: 0 : if (!strcmp(acs, "1") || !strcmp(acs, "yes"))
879 : 0 : always_copy_stacks = 1;
880 [ # # # # ]: 0 : else if (!strcmp(acs, "0") || !strcmp(acs, "no"))
881 : 0 : always_copy_stacks = 0;
882 : : else {
883 : 0 : jl_safe_printf("invalid JULIA_COPY_STACKS value: %s\n", acs);
884 : 0 : exit(1);
885 : : }
886 : : }
887 : : #ifndef COPY_STACKS
888 : : if (always_copy_stacks) {
889 : : jl_safe_printf("Julia built without COPY_STACKS support");
890 : : exit(1);
891 : : }
892 : : #endif
893 : 573 : }
894 : :
895 : 1266530 : STATIC_OR_JS void NOINLINE JL_NORETURN start_task(void)
896 : : {
897 : 1266530 : CFI_NORETURN
898 : : // this runs the first time we switch to a task
899 : 1266560 : sanitizer_finish_switch_fiber();
900 : : #ifdef __clang_gcanalyzer__
901 : : jl_task_t *ct = jl_get_current_task();
902 : : #else
903 : 1266250 : jl_task_t *ct = jl_current_task;
904 : : #endif
905 : 1266250 : jl_ptls_t ptls = ct->ptls;
906 : : jl_value_t *res;
907 [ - + ]: 1266250 : assert(ptls->finalizers_inhibited == 0);
908 : :
909 : : #ifdef MIGRATE_TASKS
910 : 1266250 : jl_task_t *pt = ptls->previous_task;
911 : 1266250 : ptls->previous_task = NULL;
912 [ + + + # ]: 1266250 : if (!pt->sticky && !pt->copy_stack)
913 : 554233 : jl_atomic_store_release(&pt->tid, -1);
914 : : #endif
915 : :
916 : 1266250 : ct->started = 1;
917 : : JL_PROBE_RT_START_TASK(ct);
918 [ + + ]: 1266250 : if (jl_atomic_load_relaxed(&ct->_isexception)) {
919 : 3 : record_backtrace(ptls, 0);
920 : 3 : jl_push_excstack(&ct->excstack, ct->result,
921 : 3 : ptls->bt_data, ptls->bt_size);
922 : 3 : res = ct->result;
923 : : }
924 : : else {
925 [ + + + + ]: 2528200 : JL_TRY {
926 [ + + ]: 1264210 : if (ptls->defer_signal) {
927 : 10 : ptls->defer_signal = 0;
928 : 10 : jl_sigint_safepoint(ptls);
929 : : }
930 : : JL_TIMING(ROOT);
931 : 1264210 : res = jl_apply(&ct->start, 1);
932 : : }
933 [ - + ]: 300 : JL_CATCH {
934 : 390 : res = jl_current_exception();
935 : 390 : jl_atomic_store_relaxed(&ct->_isexception, 1);
936 : 390 : goto skip_pop_exception;
937 : : }
938 : 1262400 : skip_pop_exception:;
939 : : }
940 : 1262410 : ct->result = res;
941 : 1262410 : jl_gc_wb(ct, ct->result);
942 : 1262650 : jl_finish_task(ct);
943 : : jl_gc_debug_critical_error();
944 : : abort();
945 : : }
946 : :
947 : :
948 : : #if defined(JL_HAVE_UCONTEXT)
949 : : #ifdef _OS_WINDOWS_
950 : : #define setcontext jl_setcontext
951 : : #define swapcontext jl_swapcontext
952 : : #define makecontext jl_makecontext
953 : : #endif
954 : : static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) JL_NOTSAFEPOINT
955 : : {
956 : : #ifndef _OS_WINDOWS_
957 : : int r = getcontext(t);
958 : : if (r != 0)
959 : : jl_error("getcontext failed");
960 : : #endif
961 : : void *stk = jl_malloc_stack(ssize, owner);
962 : : if (stk == NULL)
963 : : return NULL;
964 : : t->uc_stack.ss_sp = stk;
965 : : t->uc_stack.ss_size = *ssize;
966 : : #ifdef _OS_WINDOWS_
967 : : makecontext(t, &start_task);
968 : : #else
969 : : t->uc_link = NULL;
970 : : makecontext(t, &start_task, 0);
971 : : #endif
972 : : return (char*)stk;
973 : : }
974 : : static void jl_start_fiber_set(jl_ucontext_t *t)
975 : : {
976 : : setcontext(&t->ctx);
977 : : }
978 : : static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t)
979 : : {
980 : : assert(lastt);
981 : : tsan_switch_to_ctx(t);
982 : : swapcontext(&lastt->ctx, &t->ctx);
983 : : }
984 : : static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t)
985 : : {
986 : : tsan_switch_to_ctx(t);
987 : : swapcontext(&lastt->ctx, &t->ctx);
988 : : }
989 : : static void jl_set_fiber(jl_ucontext_t *t)
990 : : {
991 : : setcontext(&t->ctx);
992 : : }
993 : : #endif
994 : :
995 : : #if defined(JL_HAVE_UNW_CONTEXT) || defined(JL_HAVE_ASM)
996 : 1268500 : static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner)
997 : : {
998 : 1268500 : char *stkbuf = (char*)jl_malloc_stack(ssize, owner);
999 [ + + ]: 1267960 : if (stkbuf == NULL)
1000 : 171953 : return NULL;
1001 : : #ifndef __clang_gcanalyzer__
1002 : 1096010 : ((char**)t)[0] = stkbuf; // stash the stack pointer somewhere for start_fiber
1003 : 1096010 : ((size_t*)t)[1] = *ssize; // stash the stack size somewhere for start_fiber
1004 : : #endif
1005 : 1096010 : return stkbuf;
1006 : : }
1007 : : #endif
1008 : :
1009 : : #if defined(JL_HAVE_UNW_CONTEXT)
1010 : : static inline void jl_unw_swapcontext(unw_context_t *old, unw_cursor_t *c)
1011 : : {
1012 : : volatile int returns = 0;
1013 : : int r = unw_getcontext(old);
1014 : : if (++returns == 2) // r is garbage after the first return
1015 : : return;
1016 : : if (r != 0 || returns != 1)
1017 : : abort();
1018 : : unw_resume(c);
1019 : : }
1020 : : static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t)
1021 : : {
1022 : : unw_cursor_t c;
1023 : : int r = unw_init_local(&c, &t->ctx);
1024 : : if (r < 0)
1025 : : abort();
1026 : : jl_unw_swapcontext(&lastt->ctx, &c);
1027 : : }
1028 : : static void jl_set_fiber(jl_ucontext_t *t)
1029 : : {
1030 : : unw_cursor_t c;
1031 : : int r = unw_init_local(&c, &t->ctx);
1032 : : if (r < 0)
1033 : : abort();
1034 : : unw_resume(&c);
1035 : : }
1036 : : #elif defined(JL_HAVE_ASM)
1037 : 467530 : static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t)
1038 : : {
1039 [ + + ]: 467530 : if (jl_setjmp(lastt->ctx.uc_mcontext, 0))
1040 : 466771 : return;
1041 : 467357 : tsan_switch_to_ctx(t);
1042 : 467357 : jl_set_fiber(t); // doesn't return
1043 : : }
1044 : 1123570 : static void jl_set_fiber(jl_ucontext_t *t)
1045 : : {
1046 : 1123570 : jl_longjmp(t->ctx.uc_mcontext, 1);
1047 : : }
1048 : : #endif
1049 : :
1050 : : #if defined(JL_HAVE_UNW_CONTEXT) && !defined(JL_HAVE_ASM)
1051 : : #if defined(_CPU_X86_) || defined(_CPU_X86_64_)
1052 : : #define PUSH_RET(ctx, stk) \
1053 : : do { \
1054 : : stk -= sizeof(uintptr_t); \
1055 : : *(uintptr_t*)stk = 0; /* push null RIP/EIP onto the stack */ \
1056 : : } while (0)
1057 : : #elif defined(_CPU_ARM_)
1058 : : #define PUSH_RET(ctx, stk) \
1059 : : if (unw_set_reg(ctx, UNW_ARM_R14, 0)) /* put NULL into the LR */ \
1060 : : abort();
1061 : : #else
1062 : : #error please define how to simulate a CALL on this platform
1063 : : #endif
1064 : : static void jl_start_fiber_set(jl_ucontext_t *t)
1065 : : {
1066 : : unw_cursor_t c;
1067 : : char *stk = ((char**)&t->ctx)[0];
1068 : : size_t ssize = ((size_t*)&t->ctx)[1];
1069 : : uintptr_t fn = (uintptr_t)&start_task;
1070 : : stk += ssize;
1071 : : int r = unw_getcontext(&t->ctx);
1072 : : if (r)
1073 : : abort();
1074 : : if (unw_init_local(&c, &t->ctx))
1075 : : abort();
1076 : : PUSH_RET(&c, stk);
1077 : : #if defined __linux__
1078 : : #error savannah nongnu libunwind is incapable of setting UNW_REG_SP, as required
1079 : : #endif
1080 : : if (unw_set_reg(&c, UNW_REG_SP, (uintptr_t)stk))
1081 : : abort();
1082 : : if (unw_set_reg(&c, UNW_REG_IP, fn))
1083 : : abort();
1084 : : unw_resume(&c); // (doesn't return)
1085 : : }
1086 : : static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t)
1087 : : {
1088 : : assert(lastt);
1089 : : unw_cursor_t c;
1090 : : char *stk = ((char**)&t->ctx)[0];
1091 : : size_t ssize = ((size_t*)&t->ctx)[1];
1092 : : uintptr_t fn = (uintptr_t)&start_task;
1093 : : stk += ssize;
1094 : : volatile int returns = 0;
1095 : : int r = unw_getcontext(&lastt->ctx);
1096 : : if (++returns == 2) // r is garbage after the first return
1097 : : return;
1098 : : if (r != 0 || returns != 1)
1099 : : abort();
1100 : : r = unw_getcontext(&t->ctx);
1101 : : if (r != 0)
1102 : : abort();
1103 : : if (unw_init_local(&c, &t->ctx))
1104 : : abort();
1105 : : PUSH_RET(&c, stk);
1106 : : if (unw_set_reg(&c, UNW_REG_SP, (uintptr_t)stk))
1107 : : abort();
1108 : : if (unw_set_reg(&c, UNW_REG_IP, fn))
1109 : : abort();
1110 : : jl_unw_swapcontext(&lastt->ctx, &c);
1111 : : }
1112 : : #endif
1113 : :
1114 : : #if defined(JL_HAVE_ASM)
1115 : 640672 : static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t)
1116 : : {
1117 [ - + ]: 640672 : assert(lastt);
1118 : : #ifdef JL_HAVE_UNW_CONTEXT
1119 : : volatile int returns = 0;
1120 : : int r = unw_getcontext(&lastt->ctx);
1121 : : if (++returns == 2) // r is garbage after the first return
1122 : : return;
1123 : : if (r != 0 || returns != 1)
1124 : : abort();
1125 : : #else
1126 [ + + ]: 640672 : if (jl_setjmp(lastt->ctx.uc_mcontext, 0))
1127 : 639770 : return;
1128 : : #endif
1129 : 639874 : tsan_switch_to_ctx(t);
1130 : 639874 : jl_start_fiber_set(t); // doesn't return
1131 : : }
1132 : 1266360 : static void jl_start_fiber_set(jl_ucontext_t *t)
1133 : : {
1134 : 1266360 : char *stk = ((char**)&t->ctx)[0];
1135 : 1266360 : size_t ssize = ((size_t*)&t->ctx)[1];
1136 : 1266360 : uintptr_t fn = (uintptr_t)&start_task;
1137 : 1266360 : stk += ssize;
1138 : : #ifdef _CPU_X86_64_
1139 : 1266360 : asm volatile (
1140 : : " movq %0, %%rsp;\n"
1141 : : " movq %1, %%rax;\n"
1142 : : " xorq %%rbp, %%rbp;\n"
1143 : : " push %%rbp;\n" // instead of RSP
1144 : : " jmpq *%%rax;\n" // call `fn` with fake stack frame
1145 : : " ud2"
1146 : : : : "r"(stk), "r"(fn) : "memory" );
1147 : : #elif defined(_CPU_X86_)
1148 : : asm volatile (
1149 : : " movl %0, %%esp;\n"
1150 : : " movl %1, %%eax;\n"
1151 : : " xorl %%ebp, %%ebp;\n"
1152 : : " push %%ebp;\n" // instead of ESP
1153 : : " jmpl *%%eax;\n" // call `fn` with fake stack frame
1154 : : " ud2"
1155 : : : : "r"(stk), "r"(fn) : "memory" );
1156 : : #elif defined(_CPU_AARCH64_)
1157 : : asm volatile(
1158 : : " mov sp, %0;\n"
1159 : : " mov x29, xzr;\n" // Clear link register (x29) and frame pointer
1160 : : " mov x30, xzr;\n" // (x30) to terminate unwinder.
1161 : : " br %1;\n" // call `fn` with fake stack frame
1162 : : " brk #0x1" // abort
1163 : : : : "r" (stk), "r"(fn) : "memory" );
1164 : : #elif defined(_CPU_ARM_)
1165 : : // A "i" constraint on `&start_task` works only on clang and not on GCC.
1166 : : asm(" mov sp, %0;\n"
1167 : : " mov lr, #0;\n" // Clear link register (lr) and frame pointer
1168 : : " mov fp, #0;\n" // (fp) to terminate unwinder.
1169 : : " bx %1;\n" // call `fn` with fake stack frame. While `bx` can change
1170 : : // the processor mode to thumb, this will never happen
1171 : : // because all our addresses are word-aligned.
1172 : : " udf #0" // abort
1173 : : : : "r" (stk), "r"(fn) : "memory" );
1174 : : #elif defined(_CPU_PPC64_)
1175 : : // N.B.: There is two iterations of the PPC64 ABI.
1176 : : // v2 is current and used here. Make sure you have the
1177 : : // correct version of the ABI reference when working on this code.
1178 : : asm volatile(
1179 : : // Move stack (-0x30 for initial stack frame) to stack pointer
1180 : : " addi 1, %0, -0x30;\n"
1181 : : // Build stack frame
1182 : : // Skip local variable save area
1183 : : " std 2, 0x28(1);\n" // Save TOC
1184 : : // Clear link editor/compiler words
1185 : : " std 0, 0x20(1);\n"
1186 : : " std 0, 0x18(1);\n"
1187 : : // Clear LR/CR save area
1188 : : " std 0, 0x10(1);\n"
1189 : : " std 0, 0x8(1);\n"
1190 : : " std 0, 0x0(1); \n" // Clear back link to terminate unwinder
1191 : : " mtlr 0; \n" // Clear link register
1192 : : " mr 12, %1; \n" // Set up target global entry point
1193 : : " mtctr 12; \n" // Move jump target to counter register
1194 : : " bctr; \n" // branch to counter (lr update disabled)
1195 : : " trap; \n"
1196 : : : : "r"(stk), "r"(fn) : "memory");
1197 : : #else
1198 : : #error JL_HAVE_ASM defined but not implemented for this CPU type
1199 : : #endif
1200 : 0 : __builtin_unreachable();
1201 : : }
1202 : : #endif
1203 : :
1204 : : #if defined(JL_HAVE_SIGALTSTACK)
1205 : : #if defined(_COMPILER_TSAN_ENABLED_)
1206 : : #error TSAN support not currently implemented for this tasking model
1207 : : #endif
1208 : :
1209 : : static void start_basefiber(int sig)
1210 : : {
1211 : : jl_ptls_t ptls = jl_current_task->ptls;
1212 : : if (jl_setjmp(ptls->base_ctx.uc_mcontext, 0))
1213 : : start_task(); // sanitizer_finish_switch_fiber is part of start_task
1214 : : }
1215 : : static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner)
1216 : : {
1217 : : stack_t uc_stack, osigstk;
1218 : : struct sigaction sa, osa;
1219 : : sigset_t set, oset;
1220 : : void *stk = jl_malloc_stack(ssize, owner);
1221 : : if (stk == NULL)
1222 : : return NULL;
1223 : : // setup
1224 : : jl_ptls_t ptls = jl_current_task->ptls;
1225 : : _jl_ucontext_t base_ctx;
1226 : : memcpy(&base_ctx, &ptls->base_ctx, sizeof(base_ctx));
1227 : : sigfillset(&set);
1228 : : if (sigprocmask(SIG_BLOCK, &set, &oset) != 0) {
1229 : : jl_free_stack(stk, *ssize);
1230 : : jl_error("sigprocmask failed");
1231 : : }
1232 : : uc_stack.ss_sp = stk;
1233 : : uc_stack.ss_size = *ssize;
1234 : : uc_stack.ss_flags = 0;
1235 : : if (sigaltstack(&uc_stack, &osigstk) != 0) {
1236 : : jl_free_stack(stk, *ssize);
1237 : : jl_error("sigaltstack failed");
1238 : : }
1239 : : memset(&sa, 0, sizeof(sa));
1240 : : sigemptyset(&sa.sa_mask);
1241 : : sa.sa_handler = start_basefiber;
1242 : : sa.sa_flags = SA_ONSTACK;
1243 : : if (sigaction(SIGUSR2, &sa, &osa) != 0) {
1244 : : jl_free_stack(stk, *ssize);
1245 : : jl_error("sigaction failed");
1246 : : }
1247 : : // emit signal
1248 : : pthread_kill(pthread_self(), SIGUSR2); // initializes jl_basectx
1249 : : sigdelset(&set, SIGUSR2);
1250 : : sigsuspend(&set);
1251 : : // cleanup
1252 : : if (sigaction(SIGUSR2, &osa, NULL) != 0) {
1253 : : jl_free_stack(stk, *ssize);
1254 : : jl_error("sigaction failed");
1255 : : }
1256 : : if (osigstk.ss_size < MINSTKSZ && (osigstk.ss_flags | SS_DISABLE))
1257 : : osigstk.ss_size = MINSTKSZ;
1258 : : if (sigaltstack(&osigstk, NULL) != 0) {
1259 : : jl_free_stack(stk, *ssize);
1260 : : jl_error("sigaltstack failed");
1261 : : }
1262 : : if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) {
1263 : : jl_free_stack(stk, *ssize);
1264 : : jl_error("sigprocmask failed");
1265 : : }
1266 : : if (&ptls->base_ctx != t) {
1267 : : memcpy(&t, &ptls->base_ctx, sizeof(base_ctx));
1268 : : memcpy(&ptls->base_ctx, &base_ctx, sizeof(base_ctx)); // restore COPY_STACKS context
1269 : : }
1270 : : return (char*)stk;
1271 : : }
1272 : : static void jl_start_fiber_set(jl_ucontext_t *t) {
1273 : : jl_longjmp(t->ctx.uc_mcontext, 1); // (doesn't return)
1274 : : }
1275 : : static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t)
1276 : : {
1277 : : assert(lastt);
1278 : : if (lastt && jl_setjmp(lastt->ctx.uc_mcontext, 0))
1279 : : return;
1280 : : tsan_switch_to_ctx(t);
1281 : : jl_start_fiber_set(t);
1282 : : }
1283 : : static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t)
1284 : : {
1285 : : if (jl_setjmp(lastt->ctx.uc_mcontext, 0))
1286 : : return;
1287 : : tsan_switch_to_ctx(t);
1288 : : jl_start_fiber_set(t); // doesn't return
1289 : : }
1290 : : static void jl_set_fiber(jl_ucontext_t *t)
1291 : : {
1292 : : jl_longjmp(t->ctx.uc_mcontext, 1);
1293 : : }
1294 : : #endif
1295 : :
1296 : : #if defined(JL_HAVE_ASYNCIFY)
1297 : : #if defined(_COMPILER_TSAN_ENABLED_)
1298 : : #error TSAN support not currently implemented for this tasking model
1299 : : #endif
1300 : :
1301 : : static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) JL_NOTSAFEPOINT
1302 : : {
1303 : : void *stk = jl_malloc_stack(ssize, owner);
1304 : : if (stk == NULL)
1305 : : return NULL;
1306 : : t->stackbottom = stk;
1307 : : t->stacktop = ((char*)stk) + *ssize;
1308 : : return (char*)stk;
1309 : : }
1310 : : // jl_*_fiber implemented in js
1311 : : #endif
1312 : :
1313 : : // Initialize a root task using the given stack.
1314 : 692 : jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi)
1315 : : {
1316 [ - + ]: 692 : assert(ptls->root_task == NULL);
1317 : : // We need `gcstack` in `Task` to allocate Julia objects; *including* the `Task` type.
1318 : : // However, to allocate a `Task` via `jl_gc_alloc` as done in `jl_init_root_task`,
1319 : : // we need the `Task` type itself. We use stack-allocated "raw" `jl_task_t` struct to
1320 : : // workaround this chicken-and-egg problem. Note that this relies on GC to be turned
1321 : : // off as GC fails because we don't/can't allocate the type tag.
1322 : : struct {
1323 : : jl_value_t *type;
1324 : : jl_task_t value;
1325 : 692 : } bootstrap_task = {0};
1326 : 692 : jl_set_pgcstack(&bootstrap_task.value.gcstack);
1327 : 692 : bootstrap_task.value.ptls = ptls;
1328 [ + + ]: 692 : if (jl_nothing == NULL) // make a placeholder
1329 : 573 : jl_nothing = jl_gc_permobj(0, jl_nothing_type);
1330 : 692 : jl_task_t *ct = (jl_task_t*)jl_gc_alloc(ptls, sizeof(jl_task_t), jl_task_type);
1331 : 692 : memset(ct, 0, sizeof(jl_task_t));
1332 : 692 : void *stack = stack_lo;
1333 : 692 : size_t ssize = (char*)stack_hi - (char*)stack_lo;
1334 : : #ifndef _OS_WINDOWS_
1335 [ + + ]: 692 : if (ptls->tid == 0) {
1336 : 573 : stack = (void*)((char*)stack - ROOT_TASK_STACK_ADJUSTMENT); // offset our guess of the address of the bottom of stack to cover the guard pages too
1337 : 573 : ssize += ROOT_TASK_STACK_ADJUSTMENT; // sizeof stack is known exactly, but not where we are in that stack
1338 : : }
1339 : : #endif
1340 [ - + ]: 692 : if (always_copy_stacks) {
1341 : 0 : ct->copy_stack = 1;
1342 : 0 : ct->stkbuf = NULL;
1343 : 0 : ct->bufsz = 0;
1344 : : }
1345 : : else {
1346 : 692 : ct->copy_stack = 0;
1347 : 692 : ct->stkbuf = stack;
1348 : 692 : ct->bufsz = ssize;
1349 : : }
1350 : 692 : ct->started = 1;
1351 : 692 : ct->next = jl_nothing;
1352 : 692 : ct->queue = jl_nothing;
1353 : 692 : ct->tls = jl_nothing;
1354 : 692 : jl_atomic_store_relaxed(&ct->_state, JL_TASK_STATE_RUNNABLE);
1355 : 692 : ct->start = NULL;
1356 : 692 : ct->result = jl_nothing;
1357 : 692 : ct->donenotify = jl_nothing;
1358 : 692 : jl_atomic_store_relaxed(&ct->_isexception, 0);
1359 : 692 : ct->logstate = jl_nothing;
1360 : 692 : ct->eh = NULL;
1361 : 692 : ct->gcstack = NULL;
1362 : 692 : ct->excstack = NULL;
1363 : 692 : jl_atomic_store_relaxed(&ct->tid, ptls->tid);
1364 : 692 : ct->threadpoolid = jl_threadpoolid(ptls->tid);
1365 : 692 : ct->sticky = 1;
1366 : 692 : ct->ptls = ptls;
1367 : 692 : ct->world_age = 1; // OK to run Julia code on this task
1368 : 692 : ptls->root_task = ct;
1369 : 692 : jl_atomic_store_relaxed(&ptls->current_task, ct);
1370 : : JL_GC_PROMISE_ROOTED(ct);
1371 : 692 : jl_set_pgcstack(&ct->gcstack);
1372 [ - + ]: 692 : assert(jl_current_task == ct);
1373 : :
1374 : : #ifdef _COMPILER_TSAN_ENABLED_
1375 : : ct->ctx.tsan_state = __tsan_get_current_fiber();
1376 : : #endif
1377 : :
1378 : : #ifdef COPY_STACKS
1379 : : // initialize the base_ctx from which all future copy_stacks will be copies
1380 [ - + ]: 692 : if (always_copy_stacks) {
1381 : : // when this is set, we will attempt to corrupt the process stack to switch tasks,
1382 : : // although this is unreliable, and thus not recommended
1383 : 0 : ptls->stackbase = stack_hi;
1384 : 0 : ptls->stacksize = ssize;
1385 : : #ifdef _OS_WINDOWS_
1386 : : ptls->copy_stack_ctx.uc_stack.ss_sp = stack_hi;
1387 : : ptls->copy_stack_ctx.uc_stack.ss_size = ssize;
1388 : : #endif
1389 [ # # ]: 0 : if (jl_setjmp(ptls->copy_stack_ctx.uc_mcontext, 0))
1390 : 0 : start_task(); // sanitizer_finish_switch_fiber is part of start_task
1391 : 0 : return ct;
1392 : : }
1393 : 692 : ssize = JL_STACK_SIZE;
1394 : 692 : char *stkbuf = jl_alloc_fiber(&ptls->base_ctx, &ssize, NULL);
1395 : 692 : ptls->stackbase = stkbuf + ssize;
1396 : 692 : ptls->stacksize = ssize;
1397 : : #endif
1398 : :
1399 [ + - ]: 692 : if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON)
1400 : 692 : jl_install_thread_signal_handler(ptls);
1401 : :
1402 : 692 : return ct;
1403 : : }
1404 : :
1405 : 4 : JL_DLLEXPORT int jl_is_task_started(jl_task_t *t) JL_NOTSAFEPOINT
1406 : : {
1407 : 4 : return t->started;
1408 : : }
1409 : :
1410 : 3029070 : JL_DLLEXPORT int16_t jl_get_task_tid(jl_task_t *t) JL_NOTSAFEPOINT
1411 : : {
1412 : 3029070 : return jl_atomic_load_relaxed(&t->tid);
1413 : : }
1414 : :
1415 : 699193 : JL_DLLEXPORT int8_t jl_get_task_threadpoolid(jl_task_t *t)
1416 : : {
1417 : 699193 : return t->threadpoolid;
1418 : : }
1419 : :
1420 : :
1421 : : #ifdef _OS_WINDOWS_
1422 : : #if defined(_CPU_X86_)
1423 : : extern DWORD32 __readgsdword(int);
1424 : : extern DWORD32 __readgs(void);
1425 : : #endif
1426 : : JL_DLLEXPORT void jl_gdb_dump_threadinfo(void)
1427 : : {
1428 : : #if defined(_CPU_X86_64_)
1429 : : DWORD64 gs0 = __readgsqword(0x0);
1430 : : DWORD64 gs8 = __readgsqword(0x8);
1431 : : DWORD64 gs16 = __readgsqword(0x10);
1432 : : jl_safe_printf("ThreadId: %u, Stack: %p -- %p to %p, SEH: %p\n",
1433 : : (unsigned)GetCurrentThreadId(),
1434 : : jl_get_frame_addr(),
1435 : : (void*)gs8, (void*)gs16, (void*)gs0);
1436 : : #elif defined(_CPU_X86_)
1437 : : DWORD32 fs0 = __readfsdword(0x0);
1438 : : DWORD32 fs4 = __readfsdword(0x4);
1439 : : DWORD32 fs8 = __readfsdword(0x8);
1440 : : jl_safe_printf("ThreadId: %u, Stack: %p -- %p to %p, SEH: %p\n",
1441 : : (unsigned)GetCurrentThreadId(),
1442 : : jl_get_frame_addr(),
1443 : : (void*)fs4, (void*)fs8, (void*)fs0);
1444 : : if (__readgs()) { // WoW64 if GS is non-zero
1445 : : DWORD32 gs0 = __readgsdword(0x0);
1446 : : DWORD32 gs4 = __readgsdword(0x4);
1447 : : DWORD32 gs8 = __readgsdword(0x8);
1448 : : DWORD32 gs12 = __readgsdword(0xc);
1449 : : DWORD32 gs16 = __readgsdword(0x10);
1450 : : DWORD32 gs20 = __readgsdword(0x14);
1451 : : jl_safe_printf("Stack64: %p%p to %p%p, SEH64: %p%p\n",
1452 : : (void*)gs12, (void*)gs8,
1453 : : (void*)gs20, (void*)gs16,
1454 : : (void*)gs4, (void*)gs0);
1455 : : }
1456 : : #else
1457 : : jl_safe_printf("ThreadId: %u, Stack: %p\n",
1458 : : (unsigned)GetCurrentThreadId(),
1459 : : jl_get_frame_addr());
1460 : : #endif
1461 : : }
1462 : : #endif
1463 : :
1464 : :
1465 : : #ifdef __cplusplus
1466 : : }
1467 : : #endif
|