Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license
2 : :
3 : : #include <stdlib.h>
4 : : #include <stddef.h>
5 : : #include <stdio.h>
6 : : #include <inttypes.h>
7 : : #include "julia.h"
8 : : #include "julia_internal.h"
9 : : #ifndef _OS_WINDOWS_
10 : : #include <unistd.h>
11 : : #include <sys/mman.h>
12 : : #endif
13 : :
14 : : #ifdef __cplusplus
15 : : extern "C" {
16 : : #endif
17 : :
18 : : #include <threading.h>
19 : :
20 : : // Profiler control variables
21 : : // Note: these "static" variables are also used in "signals-*.c"
22 : : static volatile jl_bt_element_t *bt_data_prof = NULL;
23 : : static volatile size_t bt_size_max = 0;
24 : : static volatile size_t bt_size_cur = 0;
25 : : static volatile uint64_t nsecprof = 0;
26 : : static volatile int running = 0;
27 : : static const uint64_t GIGA = 1000000000ULL;
28 : : // Timers to take samples at intervals
29 : : JL_DLLEXPORT void jl_profile_stop_timer(void);
30 : : JL_DLLEXPORT int jl_profile_start_timer(void);
31 : :
32 : : // Any function that acquires this lock must be either a unmanaged thread
33 : : // or in the GC safe region and must NOT allocate anything through the GC
34 : : // while holding this lock.
35 : : // Certain functions in this file might be called from an unmanaged thread
36 : : // and cannot have any interaction with the julia runtime
37 : : // They also may be re-entrant, and operating while threads are paused, so we
38 : : // separately manage the re-entrant count behavior for safety across platforms
39 : : // Note that we cannot safely upgrade read->write
40 : : uv_rwlock_t debuginfo_asyncsafe;
41 : : #ifndef _OS_WINDOWS_
42 : : pthread_key_t debuginfo_asyncsafe_held;
43 : : #else
44 : : DWORD debuginfo_asyncsafe_held;
45 : : #endif
46 : :
47 : 15 : void jl_init_profile_lock(void)
48 : : {
49 : 15 : uv_rwlock_init(&debuginfo_asyncsafe);
50 : : #ifndef _OS_WINDOWS_
51 : 15 : pthread_key_create(&debuginfo_asyncsafe_held, NULL);
52 : : #else
53 : : debuginfo_asyncsafe_held = TlsAlloc();
54 : : #endif
55 : 15 : }
56 : :
57 : 384 : uintptr_t jl_lock_profile_rd_held(void)
58 : : {
59 : : #ifndef _OS_WINDOWS_
60 : 384 : return (uintptr_t)pthread_getspecific(debuginfo_asyncsafe_held);
61 : : #else
62 : : return (uintptr_t)TlsGetValue(debuginfo_asyncsafe_held);
63 : : #endif
64 : : }
65 : :
66 : 192 : void jl_lock_profile(void)
67 : : {
68 : 192 : uintptr_t held = jl_lock_profile_rd_held();
69 [ + - ]: 192 : if (held++ == 0)
70 : 192 : uv_rwlock_rdlock(&debuginfo_asyncsafe);
71 : : #ifndef _OS_WINDOWS_
72 : 192 : pthread_setspecific(debuginfo_asyncsafe_held, (void*)held);
73 : : #else
74 : : TlsSetValue(debuginfo_asyncsafe_held, (void*)held);
75 : : #endif
76 : 192 : }
77 : :
78 : 192 : JL_DLLEXPORT void jl_unlock_profile(void)
79 : : {
80 : 192 : uintptr_t held = jl_lock_profile_rd_held();
81 [ - + ]: 192 : assert(held);
82 [ + - ]: 192 : if (--held == 0)
83 : 192 : uv_rwlock_rdunlock(&debuginfo_asyncsafe);
84 : : #ifndef _OS_WINDOWS_
85 : 192 : pthread_setspecific(debuginfo_asyncsafe_held, (void*)held);
86 : : #else
87 : : TlsSetValue(debuginfo_asyncsafe_held, (void*)held);
88 : : #endif
89 : 192 : }
90 : :
91 : 161252 : void jl_lock_profile_wr(void)
92 : : {
93 : 161252 : uv_rwlock_wrlock(&debuginfo_asyncsafe);
94 : 161252 : }
95 : :
96 : 161252 : void jl_unlock_profile_wr(void)
97 : : {
98 : 161252 : uv_rwlock_wrunlock(&debuginfo_asyncsafe);
99 : 161252 : }
100 : :
101 : :
102 : : #ifndef _OS_WINDOWS_
103 : : static uint64_t profile_cong_rng_seed = 0;
104 : : static int *profile_round_robin_thread_order = NULL;
105 : : static int profile_round_robin_thread_order_size = 0;
106 : :
107 : 90 : static void jl_shuffle_int_array_inplace(int *carray, int size, uint64_t *seed)
108 : : {
109 : : // The "modern Fisher–Yates shuffle" - O(n) algorithm
110 : : // https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
111 [ - + ]: 90 : for (int i = size; i-- > 1; ) {
112 : 0 : uint64_t unbias = UINT64_MAX; // slightly biased, but i is very small
113 : 0 : size_t j = cong(i, unbias, seed);
114 : 0 : uint64_t tmp = carray[j];
115 : 0 : carray[j] = carray[i];
116 : 0 : carray[i] = tmp;
117 : : }
118 : 90 : }
119 : :
120 : :
121 : 90 : static int *profile_get_randperm(int size)
122 : : {
123 [ + + ]: 90 : if (profile_round_robin_thread_order_size < size) {
124 : 2 : free(profile_round_robin_thread_order);
125 : 2 : profile_round_robin_thread_order = (int*)malloc_s(size * sizeof(int));
126 [ + + ]: 4 : for (int i = 0; i < size; i++)
127 : 2 : profile_round_robin_thread_order[i] = i;
128 : 2 : profile_round_robin_thread_order_size = size;
129 : 2 : profile_cong_rng_seed = jl_rand();
130 : : }
131 : 90 : jl_shuffle_int_array_inplace(profile_round_robin_thread_order, size, &profile_cong_rng_seed);
132 : 90 : return profile_round_robin_thread_order;
133 : : }
134 : : #endif
135 : :
136 : :
137 : 92 : JL_DLLEXPORT int jl_profile_is_buffer_full(void)
138 : : {
139 : : // declare buffer full if there isn't enough room to take samples across all threads
140 : : #if defined(_OS_WINDOWS_)
141 : : uint64_t nthreads = 1; // windows only profiles the main thread
142 : : #else
143 : 92 : uint64_t nthreads = jl_n_threads;
144 : : #endif
145 : : // the `+ 6` is for the two block terminators `0` plus 4 metadata entries
146 : 92 : return bt_size_cur + (((JL_BT_MAX_ENTRY_SIZE + 1) + 6) * nthreads) > bt_size_max;
147 : : }
148 : :
149 : : static uint64_t jl_last_sigint_trigger = 0;
150 : : static uint64_t jl_disable_sigint_time = 0;
151 : 0 : static void jl_clear_force_sigint(void)
152 : : {
153 : 0 : jl_last_sigint_trigger = 0;
154 : 0 : }
155 : :
156 : 0 : static int jl_check_force_sigint(void)
157 : : {
158 : : static double accum_weight = 0;
159 : 0 : uint64_t cur_time = uv_hrtime();
160 : 0 : uint64_t dt = cur_time - jl_last_sigint_trigger;
161 : 0 : uint64_t last_t = jl_last_sigint_trigger;
162 : 0 : jl_last_sigint_trigger = cur_time;
163 [ # # ]: 0 : if (last_t == 0) {
164 : 0 : accum_weight = 0;
165 : 0 : return 0;
166 : : }
167 : 0 : double new_weight = accum_weight * exp(-(dt / 1e9)) + 0.3;
168 [ # # ]: 0 : if (!isnormal(new_weight))
169 : 0 : new_weight = 0;
170 : 0 : accum_weight = new_weight;
171 [ # # ]: 0 : if (new_weight > 1) {
172 : 0 : jl_disable_sigint_time = cur_time + (uint64_t)0.5e9;
173 : 0 : return 1;
174 : : }
175 : 0 : jl_disable_sigint_time = 0;
176 : 0 : return 0;
177 : : }
178 : :
179 : : #ifndef _OS_WINDOWS_
180 : : // Not thread local, should only be accessed by the signal handler thread.
181 : : static volatile int jl_sigint_passed = 0;
182 : : static sigset_t jl_sigint_sset;
183 : : #endif
184 : :
185 : 0 : static int jl_ignore_sigint(void)
186 : : {
187 : : // On Unix, we get the SIGINT before the debugger which makes it very
188 : : // hard to interrupt a running process in the debugger with `Ctrl-C`.
189 : : // Manually raise a `SIGINT` on current thread with the signal temporarily
190 : : // unblocked and use it's behavior to decide if we need to handle the signal.
191 : : #ifndef _OS_WINDOWS_
192 : 0 : jl_sigint_passed = 0;
193 : 0 : pthread_sigmask(SIG_UNBLOCK, &jl_sigint_sset, NULL);
194 : : // This can swallow an external `SIGINT` but it's not an issue
195 : : // since we don't deliver the same number of signals anyway.
196 : 0 : pthread_kill(pthread_self(), SIGINT);
197 : 0 : pthread_sigmask(SIG_BLOCK, &jl_sigint_sset, NULL);
198 [ # # ]: 0 : if (!jl_sigint_passed)
199 : 0 : return 1;
200 : : #endif
201 : : // Force sigint requires pressing `Ctrl-C` repeatedly.
202 : : // Ignore sigint for a short time after that to avoid rethrowing sigint too
203 : : // quickly again. (Code that has this issue is inherently racy but this is
204 : : // an interactive feature anyway.)
205 [ # # # # ]: 0 : return jl_disable_sigint_time && jl_disable_sigint_time > uv_hrtime();
206 : : }
207 : :
208 : : static int exit_on_sigint = 0;
209 : 8 : JL_DLLEXPORT void jl_exit_on_sigint(int on)
210 : : {
211 : 8 : exit_on_sigint = on;
212 : 8 : }
213 : :
214 : : static uintptr_t jl_get_pc_from_ctx(const void *_ctx);
215 : : void jl_show_sigill(void *_ctx);
216 : : #if defined(_CPU_X86_64_) || defined(_CPU_X86_) \
217 : : || (defined(_OS_LINUX_) && defined(_CPU_AARCH64_)) \
218 : : || (defined(_OS_LINUX_) && defined(_CPU_ARM_))
219 : 0 : static size_t jl_safe_read_mem(const volatile char *ptr, char *out, size_t len)
220 : : {
221 : 0 : jl_jmp_buf *old_buf = jl_get_safe_restore();
222 : : jl_jmp_buf buf;
223 : 0 : jl_set_safe_restore(&buf);
224 : 0 : volatile size_t i = 0;
225 [ # # ]: 0 : if (!jl_setjmp(buf, 0)) {
226 [ # # ]: 0 : for (; i < len; i++) {
227 : 0 : out[i] = ptr[i];
228 : : }
229 : : }
230 : 0 : jl_set_safe_restore(old_buf);
231 : 0 : return i;
232 : : }
233 : : #endif
234 : :
235 : : static double profile_autostop_time = -1.0;
236 : : static double profile_peek_duration = 1.0; // seconds
237 : :
238 : 0 : double jl_get_profile_peek_duration(void)
239 : : {
240 : 0 : return profile_peek_duration;
241 : : }
242 : 0 : void jl_set_profile_peek_duration(double t)
243 : : {
244 : 0 : profile_peek_duration = t;
245 : 0 : }
246 : :
247 : : uintptr_t profile_show_peek_cond_loc;
248 : 10 : JL_DLLEXPORT void jl_set_peek_cond(uintptr_t cond)
249 : : {
250 : 10 : profile_show_peek_cond_loc = cond;
251 : 10 : }
252 : :
253 : 90 : static void jl_check_profile_autostop(void)
254 : : {
255 [ - + - - ]: 90 : if ((profile_autostop_time != -1.0) && (jl_hrtime() > profile_autostop_time)) {
256 : 0 : profile_autostop_time = -1.0;
257 : 0 : jl_profile_stop_timer();
258 : 0 : jl_safe_printf("\n==============================================================\n");
259 : 0 : jl_safe_printf("Profile collected. A report will print at the next yield point\n");
260 : 0 : jl_safe_printf("==============================================================\n\n");
261 : 0 : uv_async_send((uv_async_t*)profile_show_peek_cond_loc);
262 : : }
263 : 90 : }
264 : :
265 : : #if defined(_WIN32)
266 : : #include "signals-win.c"
267 : : #else
268 : : #include "signals-unix.c"
269 : : #endif
270 : :
271 : 0 : static uintptr_t jl_get_pc_from_ctx(const void *_ctx)
272 : : {
273 : : #if defined(_OS_LINUX_) && defined(_CPU_X86_64_)
274 : 0 : return ((ucontext_t*)_ctx)->uc_mcontext.gregs[REG_RIP];
275 : : #elif defined(_OS_FREEBSD_) && defined(_CPU_X86_64_)
276 : : return ((ucontext_t*)_ctx)->uc_mcontext.mc_rip;
277 : : #elif defined(_OS_LINUX_) && defined(_CPU_X86_)
278 : : return ((ucontext_t*)_ctx)->uc_mcontext.gregs[REG_EIP];
279 : : #elif defined(_OS_FREEBSD_) && defined(_CPU_X86_)
280 : : return ((ucontext_t*)_ctx)->uc_mcontext.mc_eip;
281 : : #elif defined(_OS_DARWIN_) && defined(_CPU_x86_64_)
282 : : return ((ucontext64_t*)_ctx)->uc_mcontext64->__ss.__rip;
283 : : #elif defined(_OS_DARWIN_) && defined(_CPU_AARCH64_)
284 : : return ((ucontext64_t*)_ctx)->uc_mcontext64->__ss.__pc;
285 : : #elif defined(_OS_WINDOWS_) && defined(_CPU_X86_)
286 : : return ((CONTEXT*)_ctx)->Eip;
287 : : #elif defined(_OS_WINDOWS_) && defined(_CPU_X86_64_)
288 : : return ((CONTEXT*)_ctx)->Rip;
289 : : #elif defined(_OS_LINUX_) && defined(_CPU_AARCH64_)
290 : : return ((ucontext_t*)_ctx)->uc_mcontext.pc;
291 : : #elif defined(_OS_LINUX_) && defined(_CPU_ARM_)
292 : : return ((ucontext_t*)_ctx)->uc_mcontext.arm_pc;
293 : : #else
294 : : // TODO for PPC
295 : : return 0;
296 : : #endif
297 : : }
298 : :
299 : 0 : void jl_show_sigill(void *_ctx)
300 : : {
301 : 0 : char *pc = (char*)jl_get_pc_from_ctx(_ctx);
302 : : // unsupported platform
303 [ # # ]: 0 : if (!pc)
304 : 0 : return;
305 : : #if defined(_CPU_X86_64_) || defined(_CPU_X86_)
306 : : uint8_t inst[15]; // max length of x86 instruction
307 : 0 : size_t len = jl_safe_read_mem(pc, (char*)inst, sizeof(inst));
308 : : // ud2
309 [ # # # # : 0 : if (len >= 2 && inst[0] == 0x0f && inst[1] == 0x0b) {
# # ]
310 : 0 : jl_safe_printf("Unreachable reached at %p\n", (void*)pc);
311 : : }
312 : : else {
313 : 0 : jl_safe_printf("Invalid instruction at %p: ", (void*)pc);
314 [ # # ]: 0 : for (int i = 0;i < len;i++) {
315 [ # # ]: 0 : if (i == 0) {
316 : 0 : jl_safe_printf("0x%02" PRIx8, inst[i]);
317 : : }
318 : : else {
319 : 0 : jl_safe_printf(", 0x%02" PRIx8, inst[i]);
320 : : }
321 : : }
322 : 0 : jl_safe_printf("\n");
323 : : }
324 : : #elif defined(_OS_LINUX_) && defined(_CPU_AARCH64_)
325 : : uint32_t inst = 0;
326 : : size_t len = jl_safe_read_mem(pc, (char*)&inst, 4);
327 : : if (len < 4)
328 : : jl_safe_printf("Fault when reading instruction: %d bytes read\n", (int)len);
329 : : if (inst == 0xd4200020) { // brk #0x1
330 : : // The signal might actually be SIGTRAP instead, doesn't hurt to handle it here though.
331 : : jl_safe_printf("Unreachable reached at %p\n", pc);
332 : : }
333 : : else {
334 : : jl_safe_printf("Invalid instruction at %p: 0x%08" PRIx32 "\n", pc, inst);
335 : : }
336 : : #elif defined(_OS_LINUX_) && defined(_CPU_ARM_)
337 : : ucontext_t *ctx = (ucontext_t*)_ctx;
338 : : if (ctx->uc_mcontext.arm_cpsr & (1 << 5)) {
339 : : // Thumb
340 : : uint16_t inst[2] = {0, 0};
341 : : size_t len = jl_safe_read_mem(pc, (char*)&inst, 4);
342 : : if (len < 2)
343 : : jl_safe_printf("Fault when reading Thumb instruction: %d bytes read\n", (int)len);
344 : : // LLVM and GCC uses different code for the trap...
345 : : if (inst[0] == 0xdefe || inst[0] == 0xdeff) {
346 : : // The signal might actually be SIGTRAP instead, doesn't hurt to handle it here though.
347 : : jl_safe_printf("Unreachable reached in Thumb mode at %p: 0x%04" PRIx16 "\n",
348 : : (void*)pc, inst[0]);
349 : : }
350 : : else {
351 : : jl_safe_printf("Invalid Thumb instruction at %p: 0x%04" PRIx16 ", 0x%04" PRIx16 "\n",
352 : : (void*)pc, inst[0], inst[1]);
353 : : }
354 : : }
355 : : else {
356 : : uint32_t inst = 0;
357 : : size_t len = jl_safe_read_mem(pc, (char*)&inst, 4);
358 : : if (len < 4)
359 : : jl_safe_printf("Fault when reading instruction: %d bytes read\n", (int)len);
360 : : // LLVM and GCC uses different code for the trap...
361 : : if (inst == 0xe7ffdefe || inst == 0xe7f000f0) {
362 : : // The signal might actually be SIGTRAP instead, doesn't hurt to handle it here though.
363 : : jl_safe_printf("Unreachable reached in ARM mode at %p: 0x%08" PRIx32 "\n",
364 : : (void*)pc, inst);
365 : : }
366 : : else {
367 : : jl_safe_printf("Invalid ARM instruction at %p: 0x%08" PRIx32 "\n", (void*)pc, inst);
368 : : }
369 : : }
370 : : #else
371 : : // TODO for PPC
372 : : (void)_ctx;
373 : : #endif
374 : : }
375 : :
376 : : // what to do on a critical error on a thread
377 : 0 : void jl_critical_error(int sig, bt_context_t *context, jl_task_t *ct)
378 : : {
379 [ # # ]: 0 : jl_bt_element_t *bt_data = ct ? ct->ptls->bt_data : NULL;
380 [ # # ]: 0 : size_t *bt_size = ct ? &ct->ptls->bt_size : NULL;
381 [ # # ]: 0 : size_t i, n = ct ? *bt_size : 0;
382 [ # # ]: 0 : if (sig) {
383 : : // kill this task, so that we cannot get back to it accidentally (via an untimely ^C or jlbacktrace in jl_exit)
384 : 0 : jl_set_safe_restore(NULL);
385 [ # # ]: 0 : if (ct) {
386 : 0 : ct->gcstack = NULL;
387 : 0 : ct->eh = NULL;
388 : 0 : ct->excstack = NULL;
389 : 0 : ct->ptls->locks.len = 0;
390 : 0 : ct->ptls->in_pure_callback = 0;
391 : 0 : ct->ptls->in_finalizer = 1;
392 : 0 : ct->world_age = 1;
393 : : }
394 : : #ifndef _OS_WINDOWS_
395 : : sigset_t sset;
396 : 0 : sigemptyset(&sset);
397 : : // n.b. In `abort()`, Apple's libSystem "helpfully" blocks all signals
398 : : // on all threads but SIGABRT. But we also don't know what the thread
399 : : // was doing, so unblock all critical signals so that they will crash
400 : : // hard, and not just get stuck.
401 : 0 : sigaddset(&sset, SIGSEGV);
402 : 0 : sigaddset(&sset, SIGBUS);
403 : 0 : sigaddset(&sset, SIGILL);
404 : : // also unblock fatal signals now, so we won't get back here twice
405 : 0 : sigaddset(&sset, SIGTERM);
406 : 0 : sigaddset(&sset, SIGABRT);
407 : 0 : sigaddset(&sset, SIGQUIT);
408 : : // and the original signal is now fatal too, in case it wasn't
409 : : // something already listed (?)
410 [ # # ]: 0 : if (sig != SIGINT)
411 : 0 : sigaddset(&sset, sig);
412 : 0 : pthread_sigmask(SIG_UNBLOCK, &sset, NULL);
413 : : #endif
414 : 0 : jl_safe_printf("\n[%d] signal (%d): %s\n", getpid(), sig, strsignal(sig));
415 : : }
416 : 0 : jl_safe_printf("in expression starting at %s:%d\n", jl_filename, jl_lineno);
417 [ # # # # ]: 0 : if (context && ct) {
418 : : // Must avoid extended backtrace frames here unless we're sure bt_data
419 : : // is properly rooted.
420 : 0 : *bt_size = n = rec_backtrace_ctx(bt_data, JL_MAX_BT_SIZE, context, NULL);
421 : : }
422 [ # # ]: 0 : for (i = 0; i < n; i += jl_bt_entry_size(bt_data + i)) {
423 : 0 : jl_print_bt_entry_codeloc(bt_data + i);
424 : : }
425 : 0 : jl_gc_debug_print_status();
426 : 0 : jl_gc_debug_critical_error();
427 : 0 : }
428 : :
429 : : ///////////////////////
430 : : // Utility functions //
431 : : ///////////////////////
432 : 10 : JL_DLLEXPORT int jl_profile_init(size_t maxsize, uint64_t delay_nsec)
433 : : {
434 : 10 : bt_size_max = maxsize;
435 : 10 : nsecprof = delay_nsec;
436 [ - + ]: 10 : if (bt_data_prof != NULL)
437 : 0 : free((void*)bt_data_prof);
438 : 10 : bt_data_prof = (jl_bt_element_t*) calloc(maxsize, sizeof(jl_bt_element_t));
439 [ - + - - ]: 10 : if (bt_data_prof == NULL && maxsize > 0)
440 : 0 : return -1;
441 : 10 : bt_size_cur = 0;
442 : 10 : return 0;
443 : : }
444 : :
445 : 2 : JL_DLLEXPORT uint8_t *jl_profile_get_data(void)
446 : : {
447 : 2 : return (uint8_t*) bt_data_prof;
448 : : }
449 : :
450 : 17472 : JL_DLLEXPORT size_t jl_profile_len_data(void)
451 : : {
452 : 17472 : return bt_size_cur;
453 : : }
454 : :
455 : 2 : JL_DLLEXPORT size_t jl_profile_maxlen_data(void)
456 : : {
457 : 2 : return bt_size_max;
458 : : }
459 : :
460 : 0 : JL_DLLEXPORT uint64_t jl_profile_delay_nsec(void)
461 : : {
462 : 0 : return nsecprof;
463 : : }
464 : :
465 : 2 : JL_DLLEXPORT void jl_profile_clear_data(void)
466 : : {
467 : 2 : bt_size_cur = 0;
468 : 2 : }
469 : :
470 : 0 : JL_DLLEXPORT int jl_profile_is_running(void)
471 : : {
472 : 0 : return running;
473 : : }
474 : :
475 : : #ifdef __cplusplus
476 : : }
477 : : #endif
|