void jl_throw_in_thread(int tid, mach_port_t thread, jl_value_t *exception) { unsigned int count = MACHINE_THREAD_STATE_COUNT; x86_thread_state64_t state; kern_return_t ret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&state, &count); HANDLE_MACH_ERROR("thread_get_state", ret); jl_all_task_states[tid].ptls->bt_size = rec_backtrace_ctx(jl_all_task_states[tid].ptls->bt_data, JL_MAX_BT_SIZE, (bt_context_t)&state); jl_all_task_states[tid].ptls->exception_in_transit = exception; uint64_t rsp = (uint64_t)jl_all_task_states[tid].signal_stack + sig_stack_size; rsp &= -16; // ensure 16-byte alignment // push (null) $RIP onto the stack rsp -= sizeof(void*); *(void**)rsp = NULL; state.__rsp = rsp; // set stack pointer state.__rip = (uint64_t)&jl_rethrow; // "call" the function ret = thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t)&state, count); HANDLE_MACH_ERROR("thread_set_state",ret); }
DLLEXPORT int jl_profile_start_timer(void) { kern_return_t ret; if (!profile_started) { mach_port_t self = mach_task_self(); ret = host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, (clock_serv_t *)&clk); HANDLE_MACH_ERROR("host_get_clock_service", ret); ret = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &profile_port); HANDLE_MACH_ERROR("mach_port_allocate", ret); // Alright, create a thread to serve as the listener for exceptions pthread_attr_t attr; if (pthread_attr_init(&attr) != 0) { jl_error("pthread_attr_init failed"); } pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (pthread_create(&profiler_thread, &attr, mach_profile_listener, NULL) != 0) { jl_error("pthread_create failed"); } pthread_attr_destroy(&attr); profile_started = 1; } timerprof.tv_sec = nsecprof/GIGA; timerprof.tv_nsec = nsecprof%GIGA; running = 1; ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port); HANDLE_MACH_ERROR("clock_alarm", ret); return 0; }
//exc_server uses dlsym to find symbol DLLEXPORT kern_return_t catch_exception_raise(mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, exception_data_t code, mach_msg_type_number_t code_count) { unsigned int count = MACHINE_THREAD_STATE_COUNT; unsigned int exc_count = X86_EXCEPTION_STATE64_COUNT; x86_exception_state64_t exc_state; x86_thread_state64_t state; #ifdef LIBOSXUNWIND if (thread == mach_profiler_thread) { return profiler_segv_handler(exception_port, thread, task, exception, code, code_count); } #endif kern_return_t ret = thread_get_state(thread, x86_EXCEPTION_STATE64, (thread_state_t)&exc_state, &exc_count); HANDLE_MACH_ERROR("thread_get_state", ret); uint64_t fault_addr = exc_state.__faultvaddr; #ifdef SEGV_EXCEPTION if (1) { #else if (msync((void*)(fault_addr & ~(jl_page_size - 1)), 1, MS_ASYNC) == 0) { // check if this was a valid address #endif jl_value_t *excpt; if (is_addr_on_stack((void*)fault_addr)) { excpt = jl_stackovf_exception; } #ifdef SEGV_EXCEPTION else if (msync((void*)(fault_addr & ~(jl_page_size - 1)), 1, MS_ASYNC) != 0) { // no page mapped at this address excpt = jl_segv_exception; } #endif else { if (!(exc_state.__err & WRITE_FAULT)) return KERN_INVALID_ARGUMENT; // rethrow the SEGV since it wasn't an error with writing to read-only memory excpt = jl_readonlymemory_exception; } jl_throw_in_thread(0, thread, excpt); return KERN_SUCCESS; } else { kern_return_t ret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&state, &count); HANDLE_MACH_ERROR("thread_get_state", ret); jl_critical_error(SIGSEGV, (unw_context_t*)&state, jl_bt_data, &jl_bt_size); return KERN_INVALID_ARGUMENT; } } void attach_exception_port() { kern_return_t ret; // http://www.opensource.apple.com/source/xnu/xnu-2782.1.97/osfmk/man/thread_set_exception_ports.html ret = thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, segv_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE); HANDLE_MACH_ERROR("thread_set_exception_ports", ret); }
static void allocate_segv_handler() { #ifdef JULIA_ENABLE_THREADING arraylist_new(&suspended_threads, jl_n_threads); #endif pthread_t thread; pthread_attr_t attr; kern_return_t ret; mach_port_t self = mach_task_self(); ret = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &segv_port); HANDLE_MACH_ERROR("mach_port_allocate",ret); ret = mach_port_insert_right(self, segv_port, segv_port, MACH_MSG_TYPE_MAKE_SEND); HANDLE_MACH_ERROR("mach_port_insert_right",ret); // Alright, create a thread to serve as the listener for exceptions if (pthread_attr_init(&attr) != 0) { jl_error("pthread_attr_init failed"); } pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (pthread_create(&thread, &attr, mach_segv_listener, NULL) != 0) { jl_error("pthread_create failed"); } pthread_attr_destroy(&attr); for (int16_t tid = 0;tid < jl_n_threads;tid++) { attach_exception_port(pthread_mach_thread_np(jl_all_task_states[tid].system_id)); } }
//exc_server uses dlsym to find symbol DLLEXPORT kern_return_t catch_exception_raise (mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, exception_data_t code, mach_msg_type_number_t code_count) { unsigned int count = MACHINE_THREAD_STATE_COUNT; unsigned int exc_count = X86_EXCEPTION_STATE64_COUNT; x86_thread_state64_t state, old_state; x86_exception_state64_t exc_state; kern_return_t ret; //memset(&state,0,sizeof(x86_thread_state64_t)); //memset(&exc_state,0,sizeof(x86_exception_state64_t)); ret = thread_get_state(thread,x86_EXCEPTION_STATE64,(thread_state_t)&exc_state,&exc_count); uint64_t fault_addr = exc_state.__faultvaddr; if ( #ifdef COPY_STACKS (char*)fault_addr > (char*)jl_stack_lo-3000000 && (char*)fault_addr < (char*)jl_stack_hi #else (char*)fault_addr > (char*)jl_current_task->stack-8192 && (char*)fault_addr < (char*)jl_current_task->stack+jl_current_task->ssize #endif ) { ret = thread_get_state(thread,x86_THREAD_STATE64,(thread_state_t)&state,&count); HANDLE_MACH_ERROR("thread_get_state",ret); old_state = state; // memset(&state,0,sizeof(x86_thread_state64_t)); // Setup libunwind information state.__rsp = (uint64_t)signal_stack + SIGSTKSZ; state.__rsp -= sizeof(unw_context_t); state.__rsp &= -16; unw_context_t *uc = (unw_context_t*)state.__rsp; state.__rsp -= 512; // This is for alignment. In particular note that the sizeof(void*) is necessary // since it would usually specify the return address (i.e. we are aligning the call // frame to a 16 byte boundary as required by the abi, but the stack pointer // to point to the byte beyond that. Not doing this leads to funny behavior on // the first access to an external function will fail due to stack misalignment state.__rsp &= -16; state.__rsp -= sizeof(void*); memset(uc,0,sizeof(unw_context_t)); memcpy(uc,&old_state,sizeof(x86_thread_state64_t)); state.__rdi = (uint64_t)uc; state.__rip = (uint64_t)darwin_stack_overflow_handler; state.__rbp = state.__rsp; ret = thread_set_state(thread,x86_THREAD_STATE64,(thread_state_t)&state,count); HANDLE_MACH_ERROR("thread_set_state",ret); return KERN_SUCCESS; } else { return -309; } }
static void allocate_segv_handler() { pthread_t thread; pthread_attr_t attr; kern_return_t ret; mach_port_t self = mach_task_self(); ret = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &segv_port); HANDLE_MACH_ERROR("mach_port_allocate",ret); ret = mach_port_insert_right(self, segv_port, segv_port, MACH_MSG_TYPE_MAKE_SEND); HANDLE_MACH_ERROR("mach_port_insert_right",ret); // Alright, create a thread to serve as the listener for exceptions if (pthread_attr_init(&attr) != 0) { jl_error("pthread_attr_init failed"); } pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (pthread_create(&thread, &attr, mach_segv_listener, NULL) != 0) { jl_error("pthread_create failed"); } pthread_attr_destroy(&attr); attach_exception_port(); }
static void jl_thread_suspend_and_get_state(int tid, unw_context_t **ctx, int sig) { (void)sig; mach_port_t tid_port = pthread_mach_thread_np(jl_all_task_states[tid].system_id); kern_return_t ret = thread_suspend(tid_port); HANDLE_MACH_ERROR("thread_suspend", ret); // Do the actual sampling unsigned int count = MACHINE_THREAD_STATE_COUNT; static unw_context_t state; memset(&state, 0, sizeof(unw_context_t)); // Get the state of the suspended thread ret = thread_get_state(tid_port, x86_THREAD_STATE64, (thread_state_t)&state, &count); // Initialize the unwind context with the suspend thread's state *ctx = &state; }
kern_return_t profiler_segv_handler (mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, exception_data_t code, mach_msg_type_number_t code_count) { assert(thread == mach_profiler_thread); x86_thread_state64_t state; // Not currently unwinding. Raise regular segfault if (forceDwarf == -2) return KERN_INVALID_ARGUMENT; if (forceDwarf == 0) forceDwarf = 1; else forceDwarf = -1; unsigned int count = MACHINE_THREAD_STATE_COUNT; thread_get_state(thread,x86_THREAD_STATE64,(thread_state_t)&state,&count); // don't change cs fs gs rflags uint64_t cs = state.__cs; uint64_t fs = state.__fs; uint64_t gs = state.__gs; uint64_t rflags = state.__rflags; memcpy(&state,&profiler_uc,sizeof(x86_thread_state64_t)); state.__cs = cs; state.__fs = fs; state.__gs = gs; state.__rflags = rflags; kern_return_t ret = thread_set_state(thread,x86_THREAD_STATE64,(thread_state_t)&state,count); HANDLE_MACH_ERROR("thread_set_state",ret); return KERN_SUCCESS; }
void *mach_profile_listener(void *arg) { (void)arg; int max_size = 512; mach_profiler_thread = mach_thread_self(); mig_reply_error_t *bufRequest = (mig_reply_error_t *) malloc(max_size); while (1) { kern_return_t ret = mach_msg(&bufRequest->Head, MACH_RCV_MSG, 0, max_size, profile_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); HANDLE_MACH_ERROR("mach_msg",ret); if (bt_size_cur < bt_size_max) { kern_return_t ret; // Suspend the thread so we may safely sample it ret = thread_suspend(main_thread); HANDLE_MACH_ERROR("thread_suspend",ret); // Do the actual sampling unsigned int count = MACHINE_THREAD_STATE_COUNT; x86_thread_state64_t state; // Get the state of the suspended thread ret = thread_get_state(main_thread,x86_THREAD_STATE64,(thread_state_t)&state,&count); HANDLE_MACH_ERROR("thread_get_state",ret); // Initialize the unwind context with the suspend thread's state unw_context_t uc; memset(&uc,0,sizeof(unw_context_t)); memcpy(&uc,&state,sizeof(x86_thread_state64_t)); /* * Unfortunately compact unwind info is incorrectly generated for quite a number of * libraries by quite a large number of compilers. We can fall back to DWARF unwind info * in some cases, but in quite a number of cases (especially libraries not compiled in debug * mode, only the compact unwind info may be available). Even more unfortunately, there is no * way to detect such bogus compact unwind info (other than noticing the resulting segfault). * What we do here is ugly, but necessary until the compact unwind info situation improves. * We try to use the compact unwind info and if that results in a segfault, we retry with DWARF info. * Note that in a small number of cases this may result in bogus stack traces, but at least the topmost * entry will always be correct, and the number of cases in which this is an issue is rather small. * Other than that, this implementation is not incorrect as the other thread is paused while we are profiling * and during stack unwinding we only ever read memory, but never write it. */ forceDwarf = 0; unw_getcontext(&profiler_uc); if (forceDwarf == 0) { // Save the backtrace bt_size_cur += rec_backtrace_ctx((ptrint_t*)bt_data_prof+bt_size_cur, bt_size_max-bt_size_cur-1, &uc); } else if (forceDwarf == 1) { bt_size_cur += rec_backtrace_ctx_dwarf((ptrint_t*)bt_data_prof+bt_size_cur, bt_size_max-bt_size_cur-1, &uc); } else if (forceDwarf == -1) { JL_PRINTF(JL_STDERR, "Warning: Profiler attempt to access an invalid memory location\n"); } forceDwarf = -2; // Mark the end of this block with 0 bt_data_prof[bt_size_cur] = 0; bt_size_cur++; // We're done! Resume the thread. ret = thread_resume(main_thread); HANDLE_MACH_ERROR("thread_resume",ret) if (running) { // Reset the alarm ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port); HANDLE_MACH_ERROR("clock_alarm",ret) } } }
//exc_server uses dlsym to find symbol DLLEXPORT kern_return_t catch_exception_raise(mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, exception_data_t code, mach_msg_type_number_t code_count) { unsigned int count = MACHINE_THREAD_STATE_COUNT; unsigned int exc_count = X86_EXCEPTION_STATE64_COUNT; x86_thread_state64_t state, old_state; x86_exception_state64_t exc_state; kern_return_t ret; //memset(&state,0,sizeof(x86_thread_state64_t)); //memset(&exc_state,0,sizeof(x86_exception_state64_t)); #ifdef LIBOSXUNWIND if (thread == mach_profiler_thread) { return profiler_segv_handler(exception_port,thread,task,exception,code,code_count); } #endif ret = thread_get_state(thread,x86_EXCEPTION_STATE64,(thread_state_t)&exc_state,&exc_count); HANDLE_MACH_ERROR("thread_get_state(1)",ret); uint64_t fault_addr = exc_state.__faultvaddr; #ifdef SEGV_EXCEPTION if (1) { #else if (msync((void*)(fault_addr & ~(jl_page_size - 1)), 1, MS_ASYNC) == 0) { // check if this was a valid address #endif ret = thread_get_state(thread,x86_THREAD_STATE64,(thread_state_t)&state,&count); HANDLE_MACH_ERROR("thread_get_state(2)",ret); old_state = state; // memset(&state,0,sizeof(x86_thread_state64_t)); // Setup libunwind information state.__rsp = (uint64_t)signal_stack + sig_stack_size; state.__rsp -= sizeof(unw_context_t); state.__rsp &= -16; unw_context_t *uc = (unw_context_t*)state.__rsp; state.__rsp -= 512; // This is for alignment. In particular note that the sizeof(void*) is necessary // since it would usually specify the return address (i.e., we are aligning the call // frame to a 16 byte boundary as required by the abi, but the stack pointer // to point to the byte beyond that. Not doing this leads to funny behavior on // the first access to an external function will fail due to stack misalignment state.__rsp &= -16; state.__rsp -= sizeof(void*); memset(uc,0,sizeof(unw_context_t)); memcpy(uc,&old_state,sizeof(x86_thread_state64_t)); state.__rdi = (uint64_t)uc; if (is_addr_on_stack((void*)fault_addr)) { state.__rip = (uint64_t)darwin_stack_overflow_handler; } #ifdef SEGV_EXCEPTION else if (msync((void*)(fault_addr & ~(jl_page_size - 1)), 1, MS_ASYNC) != 0) { // no page mapped at this address state.__rip = (uint64_t)darwin_segv_handler; } #endif else { if (!(exc_state.__err & WRITE_FAULT)) return KERN_INVALID_ARGUMENT; // rethrow the SEGV since it wasn't an error with writing to read-only memory state.__rip = (uint64_t)darwin_accerr_handler; } state.__rbp = state.__rsp; ret = thread_set_state(thread,x86_THREAD_STATE64,(thread_state_t)&state,count); HANDLE_MACH_ERROR("thread_set_state",ret); return KERN_SUCCESS; } else { ret = thread_get_state(thread,x86_THREAD_STATE64,(thread_state_t)&state,&count); HANDLE_MACH_ERROR("thread_get_state(3)",ret); jl_safe_printf("\nsignal (%d): %s\n", SIGSEGV, strsignal(SIGSEGV)); bt_size = rec_backtrace_ctx(bt_data, MAX_BT_SIZE, (unw_context_t*)&state); jlbacktrace(); return KERN_INVALID_ARGUMENT; } } void attach_exception_port() { kern_return_t ret; // http://www.opensource.apple.com/source/xnu/xnu-2782.1.97/osfmk/man/thread_set_exception_ports.html ret = thread_set_exception_ports(mach_thread_self(),EXC_MASK_BAD_ACCESS,segv_port,EXCEPTION_DEFAULT,MACHINE_THREAD_STATE); HANDLE_MACH_ERROR("thread_set_exception_ports",ret); }
void julia_init(char *imageFile) { jl_page_size = jl_getpagesize(); jl_find_stack_bottom(); jl_dl_handle = jl_load_dynamic_library(NULL, JL_RTLD_DEFAULT); #ifdef _OS_WINDOWS_ uv_dlopen("ntdll.dll",jl_ntdll_handle); //bypass julia's pathchecking for system dlls uv_dlopen("Kernel32.dll",jl_kernel32_handle); uv_dlopen("msvcrt.dll",jl_crtdll_handle); uv_dlopen("Ws2_32.dll",jl_winsock_handle); _jl_exe_handle.handle = GetModuleHandleA(NULL); if (!DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), (PHANDLE)&hMainThread, 0, TRUE, DUPLICATE_SAME_ACCESS )) { JL_PRINTF(JL_STDERR, "Couldn't access handle to main thread\n"); } #if defined(_CPU_X86_64_) SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); SymInitialize(GetCurrentProcess(), NULL, 1); needsSymRefreshModuleList = 0; #endif #endif jl_io_loop = uv_default_loop(); //this loop will internal events (spawining process etc.) init_stdio(); #if defined(__linux__) int ncores = jl_cpu_cores(); if (ncores > 1) { cpu_set_t cpumask; CPU_ZERO(&cpumask); for(int i=0; i < ncores; i++) { CPU_SET(i, &cpumask); } sched_setaffinity(0, sizeof(cpu_set_t), &cpumask); } #endif #ifdef JL_GC_MARKSWEEP jl_gc_init(); jl_gc_disable(); #endif jl_init_frontend(); jl_init_types(); jl_init_tasks(jl_stack_lo, jl_stack_hi-jl_stack_lo); jl_init_codegen(); jl_an_empty_cell = (jl_value_t*)jl_alloc_cell_1d(0); jl_init_serializer(); if (!imageFile) { jl_main_module = jl_new_module(jl_symbol("Main")); jl_main_module->parent = jl_main_module; jl_core_module = jl_new_module(jl_symbol("Core")); jl_core_module->parent = jl_main_module; jl_set_const(jl_main_module, jl_symbol("Core"), (jl_value_t*)jl_core_module); jl_module_using(jl_main_module, jl_core_module); jl_current_module = jl_core_module; jl_init_intrinsic_functions(); jl_init_primitives(); jl_load("boot.jl"); jl_get_builtin_hooks(); jl_boot_file_loaded = 1; jl_init_box_caches(); } if (imageFile) { JL_TRY { jl_restore_system_image(imageFile); } JL_CATCH { JL_PRINTF(JL_STDERR, "error during init:\n"); jl_show(jl_stderr_obj(), jl_exception_in_transit); JL_PRINTF(JL_STDERR, "\n"); jl_exit(1); } } // set module field of primitive types int i; void **table = jl_core_module->bindings.table; for(i=1; i < jl_core_module->bindings.size; i+=2) { if (table[i] != HT_NOTFOUND) { jl_binding_t *b = (jl_binding_t*)table[i]; if (b->value && jl_is_datatype(b->value)) { jl_datatype_t *tt = (jl_datatype_t*)b->value; tt->name->module = jl_core_module; } } } // the Main module is the one which is always open, and set as the // current module for bare (non-module-wrapped) toplevel expressions. // it does "using Base" if Base is available. if (jl_base_module != NULL) { jl_add_standard_imports(jl_main_module); } // eval() uses Main by default, so Main.eval === Core.eval jl_module_import(jl_main_module, jl_core_module, jl_symbol("eval")); jl_current_module = jl_main_module; #ifndef _OS_WINDOWS_ signal_stack = malloc(SIGSTKSZ); struct sigaction actf; memset(&actf, 0, sizeof(struct sigaction)); sigemptyset(&actf.sa_mask); actf.sa_handler = fpe_handler; actf.sa_flags = 0; if (sigaction(SIGFPE, &actf, NULL) < 0) { JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); jl_exit(1); } #if defined(_OS_LINUX_) stack_t ss; ss.ss_flags = 0; ss.ss_size = SIGSTKSZ; ss.ss_sp = signal_stack; if (sigaltstack(&ss, NULL) < 0) { JL_PRINTF(JL_STDERR, "sigaltstack: %s\n", strerror(errno)); jl_exit(1); } struct sigaction act; memset(&act, 0, sizeof(struct sigaction)); sigemptyset(&act.sa_mask); act.sa_sigaction = segv_handler; act.sa_flags = SA_ONSTACK | SA_SIGINFO; if (sigaction(SIGSEGV, &act, NULL) < 0) { JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); jl_exit(1); } if (signal(SIGPIPE,SIG_IGN) == SIG_ERR) { JL_PRINTF(JL_STDERR, "Couldn't set SIGPIPE\n"); jl_exit(1); } #elif defined (_OS_DARWIN_) kern_return_t ret; mach_port_t self = mach_task_self(); ret = mach_port_allocate(self,MACH_PORT_RIGHT_RECEIVE,&segv_port); HANDLE_MACH_ERROR("mach_port_allocate",ret); ret = mach_port_insert_right(self,segv_port,segv_port,MACH_MSG_TYPE_MAKE_SEND); HANDLE_MACH_ERROR("mach_port_insert_right",ret); // Alright, create a thread to serve as the listener for exceptions pthread_t thread; pthread_attr_t attr; if (pthread_attr_init(&attr) != 0) { JL_PRINTF(JL_STDERR, "pthread_attr_init failed"); jl_exit(1); } pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); if (pthread_create(&thread,&attr,mach_segv_listener,NULL) != 0) { JL_PRINTF(JL_STDERR, "pthread_create failed"); jl_exit(1); } pthread_attr_destroy(&attr); ret = task_set_exception_ports(self,EXC_MASK_BAD_ACCESS,segv_port,EXCEPTION_DEFAULT,MACHINE_THREAD_STATE); HANDLE_MACH_ERROR("task_set_exception_ports",ret); #endif #else if (signal(SIGFPE, (void (__cdecl *)(int))fpe_handler) == SIG_ERR) { JL_PRINTF(JL_STDERR, "Couldn't set SIGFPE\n"); jl_exit(1); } #endif #ifdef JL_GC_MARKSWEEP jl_gc_enable(); #endif }
void *mach_profile_listener(void *arg) { (void)arg; int i; const int max_size = 512; attach_exception_port(); #ifdef LIBOSXUNWIND mach_profiler_thread = mach_thread_self(); #endif mig_reply_error_t *bufRequest = (mig_reply_error_t *) malloc(max_size); while (1) { kern_return_t ret = mach_msg(&bufRequest->Head, MACH_RCV_MSG, 0, max_size, profile_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); HANDLE_MACH_ERROR("mach_msg", ret); // sample each thread, round-robin style in reverse order // (so that thread zero gets notified last) for (i = jl_n_threads; i-- > 0; ) { // if there is no space left, break early if (bt_size_cur >= bt_size_max - 1) break; unw_context_t *uc; jl_thread_suspend_and_get_state(i, &uc, -1); #ifdef LIBOSXUNWIND /* * Unfortunately compact unwind info is incorrectly generated for quite a number of * libraries by quite a large number of compilers. We can fall back to DWARF unwind info * in some cases, but in quite a number of cases (especially libraries not compiled in debug * mode, only the compact unwind info may be available). Even more unfortunately, there is no * way to detect such bogus compact unwind info (other than noticing the resulting segfault). * What we do here is ugly, but necessary until the compact unwind info situation improves. * We try to use the compact unwind info and if that results in a segfault, we retry with DWARF info. * Note that in a small number of cases this may result in bogus stack traces, but at least the topmost * entry will always be correct, and the number of cases in which this is an issue is rather small. * Other than that, this implementation is not incorrect as the other thread is paused while we are profiling * and during stack unwinding we only ever read memory, but never write it. */ forceDwarf = 0; unw_getcontext(&profiler_uc); // will resume from this point if the next lines segfault at any point if (forceDwarf == 0) { // Save the backtrace bt_size_cur += rec_backtrace_ctx((ptrint_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc); } else if (forceDwarf == 1) { bt_size_cur += rec_backtrace_ctx_dwarf((ptrint_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc); } else if (forceDwarf == -1) { jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); } forceDwarf = -2; #else bt_size_cur += rec_backtrace_ctx((ptrint_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc); #endif // Mark the end of this block with 0 bt_data_prof[bt_size_cur++] = 0; // We're done! Resume the thread. jl_thread_resume(i, 0); if (running) { // Reset the alarm kern_return_t ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port); HANDLE_MACH_ERROR("clock_alarm", ret) } } } }
//exc_server uses dlsym to find symbol JL_DLLEXPORT kern_return_t catch_exception_raise(mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, exception_data_t code, mach_msg_type_number_t code_count) { unsigned int count = MACHINE_THREAD_STATE_COUNT; unsigned int exc_count = X86_EXCEPTION_STATE64_COUNT; x86_exception_state64_t exc_state; x86_thread_state64_t state; #ifdef LIBOSXUNWIND if (thread == mach_profiler_thread) { return profiler_segv_handler(exception_port, thread, task, exception, code, code_count); } #endif int16_t tid; #ifdef JULIA_ENABLE_THREADING jl_tls_states_t *ptls = NULL; for (tid = 0;tid < jl_n_threads;tid++) { if (pthread_mach_thread_np(jl_all_task_states[tid].system_id) == thread) { ptls = jl_all_task_states[tid].ptls; break; } } if (!ptls) { // We don't know about this thread, let the kernel try another handler // instead. This shouldn't actually happen since we only register the // handler for the threads we know about. jl_safe_printf("ERROR: Exception handler triggered on unmanaged thread.\n"); return KERN_INVALID_ARGUMENT; } #else jl_tls_states_t *ptls = &jl_tls_states; tid = 0; #endif kern_return_t ret = thread_get_state(thread, x86_EXCEPTION_STATE64, (thread_state_t)&exc_state, &exc_count); HANDLE_MACH_ERROR("thread_get_state", ret); uint64_t fault_addr = exc_state.__faultvaddr; #ifdef JULIA_ENABLE_THREADING if (fault_addr == (uintptr_t)jl_gc_signal_page) { JL_LOCK_NOGC(gc_suspend); if (!jl_gc_safepoint_activated) { // GC is done before we get the message, do nothing and return JL_UNLOCK_NOGC(gc_suspend); return KERN_SUCCESS; } // Otherwise, set the gc state of the thread, suspend and record it int8_t gc_state = ptls->gc_state; ptls->gc_state = JL_GC_STATE_WAITING; uintptr_t item = tid | (((uintptr_t)gc_state) << 16); arraylist_push(&suspended_threads, (void*)item); thread_suspend(thread); JL_UNLOCK_NOGC(gc_suspend); return KERN_SUCCESS; } #endif #ifdef SEGV_EXCEPTION if (1) { #else if (msync((void*)(fault_addr & ~(jl_page_size - 1)), 1, MS_ASYNC) == 0) { // check if this was a valid address #endif jl_value_t *excpt; if (is_addr_on_stack(ptls, (void*)fault_addr)) { excpt = jl_stackovf_exception; } #ifdef SEGV_EXCEPTION else if (msync((void*)(fault_addr & ~(jl_page_size - 1)), 1, MS_ASYNC) != 0) { // no page mapped at this address excpt = jl_segv_exception; } #endif else { if (!(exc_state.__err & WRITE_FAULT)) return KERN_INVALID_ARGUMENT; // rethrow the SEGV since it wasn't an error with writing to read-only memory excpt = jl_readonlymemory_exception; } jl_throw_in_thread(tid, thread, excpt); return KERN_SUCCESS; } else { kern_return_t ret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&state, &count); HANDLE_MACH_ERROR("thread_get_state", ret); jl_critical_error(SIGSEGV, (unw_context_t*)&state, ptls->bt_data, &ptls->bt_size); return KERN_INVALID_ARGUMENT; } } static void attach_exception_port(thread_port_t thread) { kern_return_t ret; // http://www.opensource.apple.com/source/xnu/xnu-2782.1.97/osfmk/man/thread_set_exception_ports.html ret = thread_set_exception_ports(thread, EXC_MASK_BAD_ACCESS, segv_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE); HANDLE_MACH_ERROR("thread_set_exception_ports", ret); }