diff --git a/base/process.jl b/base/process.jl index 7e2f5a6d35d2d4..302387ce5b3a6d 100644 --- a/base/process.jl +++ b/base/process.jl @@ -546,7 +546,7 @@ Returns successfully if the process has already exited, but throws an error if killing the process failed for other reasons (e.g. insufficient permissions). """ -function kill(p::Process, signum::Integer) +function kill(p::Process, signum::Integer=SIGTERM) iolock_begin() if process_running(p) @assert p.handle != C_NULL @@ -558,9 +558,8 @@ function kill(p::Process, signum::Integer) iolock_end() nothing end -kill(ps::Vector{Process}) = foreach(kill, ps) -kill(ps::ProcessChain) = foreach(kill, ps.processes) -kill(p::Process) = kill(p, SIGTERM) +kill(ps::Vector{Process}, signum::Integer=SIGTERM) = for p in ps; kill(p, signum); end +kill(ps::ProcessChain, signum::Integer=SIGTERM) = kill(ps.processes, signum) """ getpid(process) -> Int32 diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index acf4b2c2240a44..24f53a0af0678e 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -23,8 +23,6 @@ using namespace llvm; -using llvm_file_magic = file_magic; - #include "julia.h" #include "julia_internal.h" #include "debuginfo.h" @@ -52,11 +50,37 @@ typedef object::SymbolRef SymRef; // and cannot have any interaction with the julia runtime static uv_rwlock_t threadsafe; -extern "C" void jl_init_debuginfo() +extern "C" void jl_init_debuginfo(void) { uv_rwlock_init(&threadsafe); } +extern "C" void jl_lock_profile(void) +{ + uv_rwlock_rdlock(&threadsafe); +} + +extern "C" void jl_unlock_profile(void) +{ + uv_rwlock_rdunlock(&threadsafe); +} + +// some actions aren't signal (especially profiler) safe so we acquire a lock +// around them to establish a mutual exclusion with unwinding from a signal +template +static void jl_profile_atomic(T f) +{ + sigset_t sset; + sigset_t oset; + sigfillset(&sset); + uv_rwlock_wrlock(&threadsafe); + pthread_sigmask(SIG_BLOCK, &sset, &oset); + f(); + pthread_sigmask(SIG_SETMASK, &oset, NULL); + uv_rwlock_wrunlock(&threadsafe); +} + + // --- storing and accessing source location metadata --- struct ObjectInfo { @@ -278,7 +302,9 @@ class JuliaJITEventListener: public JITEventListener di->u.rti.name_ptr = 0; di->u.rti.table_data = arm_exidx_addr; di->u.rti.table_len = arm_exidx_len; - _U_dyn_register(di); + jl_profile_atomic([&]() { + _U_dyn_register(di); + }); break; } #endif @@ -431,14 +457,6 @@ class JuliaJITEventListener: public JITEventListener uv_rwlock_rdlock(&threadsafe); return objectmap; } - - Optional*> trygetObjectMap() - { - if (0 == uv_rwlock_tryrdlock(&threadsafe)) { - return &objectmap; - } - return {}; - } }; JL_DLLEXPORT void ORCNotifyObjectEmitted(JITEventListener *Listener, @@ -482,7 +500,7 @@ static std::pair jl_demangle(const char *name) JL_NOTSAFEPOINT } static JuliaJITEventListener *jl_jit_events; -JITEventListener *CreateJuliaJITEventListener() +JITEventListener *CreateJuliaJITEventListener(void) { jl_jit_events = new JuliaJITEventListener(); return jl_jit_events; @@ -722,7 +740,7 @@ openDebugInfo(StringRef debuginfopath, const debug_link_info &info) auto error_splitobj = object::ObjectFile::createObjectFile( SplitFile.get().get()->getMemBufferRef(), - llvm_file_magic::unknown); + file_magic::unknown); if (!error_splitobj) { return error_splitobj.takeError(); } @@ -873,7 +891,7 @@ static objfileentry_t &find_object_file(uint64_t fbase, StringRef fname) JL_NOTS std::unique_ptr membuf = MemoryBuffer::getMemBuffer( StringRef((const char *)fbase, msize), "", false); auto origerrorobj = llvm::object::ObjectFile::createObjectFile( - membuf->getMemBufferRef(), llvm_file_magic::unknown); + membuf->getMemBufferRef(), file_magic::unknown); if (!origerrorobj) return entry; @@ -1292,11 +1310,13 @@ void register_eh_frames(uint8_t *Addr, size_t Size) // See http://lists.cs.uiuc.edu/pipermail/llvmdev/2013-April/061768.html processFDEs((char*)Addr, Size, [](const char *Entry) { if (!libc_register_frame) { - libc_register_frame = (void(*)(void*))dlsym(RTLD_NEXT,"__register_frame"); + libc_register_frame = (void(*)(void*))dlsym(RTLD_NEXT, "__register_frame"); } assert(libc_register_frame); - libc_register_frame(const_cast(Entry)); - __register_frame(const_cast(Entry)); + jl_profile_atomic([&]() { + libc_register_frame(const_cast(Entry)); + __register_frame(const_cast(Entry)); + }); }); } @@ -1304,16 +1324,19 @@ void deregister_eh_frames(uint8_t *Addr, size_t Size) { processFDEs((char*)Addr, Size, [](const char *Entry) { if (!libc_deregister_frame) { - libc_deregister_frame = (void(*)(void*))dlsym(RTLD_NEXT,"__deregister_frame"); + libc_deregister_frame = (void(*)(void*))dlsym(RTLD_NEXT, "__deregister_frame"); } assert(libc_deregister_frame); - libc_deregister_frame(const_cast(Entry)); - __deregister_frame(const_cast(Entry)); + jl_profile_atomic([&]() { + libc_deregister_frame(const_cast(Entry)); + __deregister_frame(const_cast(Entry)); + }); }); } #elif defined(_OS_LINUX_) && \ - defined(JL_UNW_HAS_FORMAT_IP) && !defined(_CPU_ARM_) + defined(JL_UNW_HAS_FORMAT_IP) && \ + !defined(_CPU_ARM_) // ARM does not have/use .eh_frame, so we handle this elsewhere #include struct unw_table_entry @@ -1499,7 +1522,9 @@ static DW_EH_PE parseCIE(const uint8_t *Addr, const uint8_t *End) void register_eh_frames(uint8_t *Addr, size_t Size) { // System unwinder - __register_frame(Addr); + jl_profile_atomic([&]() { + __register_frame(Addr); + }); // Our unwinder unw_dyn_info_t *di = new unw_dyn_info_t; // In a shared library, this is set to the address of the PLT. @@ -1610,7 +1635,7 @@ void register_eh_frames(uint8_t *Addr, size_t Size) start_ips[cur_entry] = start; cur_entry++; }); - for (size_t i = 0;i < nentries;i++) { + for (size_t i = 0; i < nentries; i++) { table[i].start_ip_offset = safe_trunc((intptr_t)start_ips[i] - (intptr_t)start_ip); } @@ -1621,27 +1646,21 @@ void register_eh_frames(uint8_t *Addr, size_t Size) di->start_ip = start_ip; di->end_ip = end_ip; - _U_dyn_register(di); + jl_profile_atomic([&]() { + _U_dyn_register(di); + }); } void deregister_eh_frames(uint8_t *Addr, size_t Size) { - __deregister_frame(Addr); - // Deregistering with our unwinder requires a lookup table to find the - // the allocated entry above (or we could look in libunwind's internal + jl_profile_atomic([&]() { + __deregister_frame(Addr); + }); + // Deregistering with our unwinder (_U_dyn_cancel) requires a lookup table + // to find the allocated entry above (or looking into libunwind's internal // data structures). } -#elif defined(_CPU_ARM_) - -void register_eh_frames(uint8_t *Addr, size_t Size) -{ -} - -void deregister_eh_frames(uint8_t *Addr, size_t Size) -{ -} - #else void register_eh_frames(uint8_t *Addr, size_t Size) @@ -1667,22 +1686,3 @@ uint64_t jl_getUnwindInfo(uint64_t dwAddr) uv_rwlock_rdunlock(&threadsafe); return ipstart; } - -extern "C" -uint64_t jl_trygetUnwindInfo(uint64_t dwAddr) -{ - // Might be called from unmanaged thread - Optional*> maybeobjmap = jl_jit_events->trygetObjectMap(); - if (maybeobjmap) { - std::map &objmap = **maybeobjmap; - std::map::iterator it = objmap.lower_bound(dwAddr); - uint64_t ipstart = 0; // ip of the start of the section (if found) - if (it != objmap.end() && dwAddr < it->first + it->second.SectionSize) { - ipstart = (uint64_t)(uintptr_t)(*it).first; - } - uv_rwlock_rdunlock(&threadsafe); - return ipstart; - } - return 0; -} - diff --git a/src/julia_internal.h b/src/julia_internal.h index 5c2a0f22f943ca..b5c3d925ee7aec 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -756,7 +756,6 @@ typedef struct { // Might be called from unmanaged thread uint64_t jl_getUnwindInfo(uint64_t dwBase); -uint64_t jl_trygetUnwindInfo(uint64_t dwBase); #ifdef _OS_WINDOWS_ #include JL_DLLEXPORT EXCEPTION_DISPOSITION __julia_personality( diff --git a/src/signal-handling.c b/src/signal-handling.c index b17dbcecefc922..1f809354361d14 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -27,6 +27,8 @@ static const uint64_t GIGA = 1000000000ULL; // Timers to take samples at intervals JL_DLLEXPORT void jl_profile_stop_timer(void); JL_DLLEXPORT int jl_profile_start_timer(void); +void jl_lock_profile(void); +void jl_unlock_profile(void); static uint64_t jl_last_sigint_trigger = 0; static uint64_t jl_disable_sigint_time = 0; diff --git a/src/signals-mach.c b/src/signals-mach.c index 2f04d25c5a5b75..0813374cca74ec 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -462,6 +462,7 @@ void *mach_profile_listener(void *arg) HANDLE_MACH_ERROR("mach_msg", ret); // sample each thread, round-robin style in reverse order // (so that thread zero gets notified last) + jl_lock_profile(); for (i = jl_n_threads; i-- > 0; ) { // if there is no space left, break early if (bt_size_cur >= bt_size_max - 1) @@ -514,6 +515,7 @@ void *mach_profile_listener(void *arg) // We're done! Resume the thread. jl_thread_resume(i, 0); } + jl_unlock_profile(); } } diff --git a/src/signals-unix.c b/src/signals-unix.c index dcf57ac54e51a6..d5a9798b9812f8 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -673,6 +673,8 @@ static void *signal_listener(void *arg) unw_context_t *signal_context; // sample each thread, round-robin style in reverse order // (so that thread zero gets notified last) + if (critical || profile) + jl_lock_profile(); for (int i = jl_n_threads; i-- > 0; ) { // notify thread to stop jl_thread_suspend_and_get_state(i, &signal_context); @@ -717,6 +719,8 @@ static void *signal_listener(void *arg) // notify thread to resume jl_thread_resume(i, sig); } + if (critical || profile) + jl_unlock_profile(); #endif // this part is async with the running of the rest of the program diff --git a/src/signals-win.c b/src/signals-win.c index 14bd250db5e4ef..9b0ecffa5a34cc 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -161,13 +161,16 @@ HANDLE hMainThread = INVALID_HANDLE_VALUE; static void jl_try_deliver_sigint(void) { jl_ptls_t ptls2 = jl_all_tls_states[0]; + jl_lock_profile(); jl_safepoint_enable_sigint(); jl_wake_libuv(); if ((DWORD)-1 == SuspendThread(hMainThread)) { // error jl_safe_printf("error: SuspendThread failed\n"); + jl_unlock_profile(); return; } + jl_unlock_profile(); int force = jl_check_force_sigint(); if (force || (!ptls2->defer_signal && ptls2->io_wait)) { jl_safepoint_consume_sigint(); @@ -338,8 +341,11 @@ static DWORD WINAPI profile_bt( LPVOID lparam ) timeout = min(max(timeout, tc.wPeriodMin*2), tc.wPeriodMax/2); Sleep(timeout); JL_LOCK_NOGC(&jl_in_stackwalk); + jl_lock_profile(); if ((DWORD)-1 == SuspendThread(hMainThread)) { fputs("failed to suspend main thread. aborting profiling.", stderr); + jl_unlock_profile(); + JL_UNLOCK_NOGC(&jl_in_stackwalk); break; } if (running) { @@ -357,6 +363,7 @@ static DWORD WINAPI profile_bt( LPVOID lparam ) if (bt_size_cur < bt_size_max) bt_data_prof[bt_size_cur++].uintptr = 0; } + jl_unlock_profile(); JL_UNLOCK_NOGC(&jl_in_stackwalk); if ((DWORD)-1 == ResumeThread(hMainThread)) { fputs("failed to resume main thread! aborting.", stderr); diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index 8ff9fa9c760051..007492745ca470 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -111,3 +111,25 @@ end end @test getline(values(fdictc)) == getline(values(fdict0)) + 2 end + +# Profile deadlocking in compilation (debuginfo registration) +let cmd = Base.julia_cmd() + script = """ + using Profile + f(::Val) = GC.safepoint() + @profile for i = 1:10^3; f(Val(i)); end + print(Profile.len_data()) + """ + p = open(`$cmd -e $script`) + t = Timer(120) do t + # should be under 10 seconds, so give it 2 minutes then report failure + println("KILLING BY PROFILE TEST WATCHDOG\n") + kill(p, Base.SIGTERM) + sleep(10) + kill(p, Base.SIGKILL) + end + s = read(p, String) + close(t) + @test success(p) + @test parse(Int, s) > 1000 +end