Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make Profile more thread/signal-safe #37101

Merged
merged 3 commits into from
Sep 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions base/process.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
160 changes: 83 additions & 77 deletions src/debuginfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@

using namespace llvm;

using llvm_file_magic = file_magic;

#include "julia.h"
#include "julia_internal.h"
#include "debuginfo.h"
Expand Down Expand Up @@ -52,11 +50,41 @@ 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 <typename T>
static void jl_profile_atomic(T f)
{
uv_rwlock_wrlock(&threadsafe);
#ifndef _OS_WINDOWS_
sigset_t sset;
sigset_t oset;
sigfillset(&sset);
pthread_sigmask(SIG_BLOCK, &sset, &oset);
#endif
f();
#ifndef _OS_WINDOWS_
pthread_sigmask(SIG_SETMASK, &oset, NULL);
#endif
uv_rwlock_wrunlock(&threadsafe);
}


// --- storing and accessing source location metadata ---

struct ObjectInfo {
Expand Down Expand Up @@ -131,13 +159,15 @@ static void create_PRUNTIME_FUNCTION(uint8_t *Code, size_t Size, StringRef fnnam
JL_UNLOCK_NOGC(&jl_in_stackwalk);
}
#if defined(_CPU_X86_64_)
if (!RtlAddFunctionTable(tbl, 1, (DWORD64)Section)) {
static int warned = 0;
if (!warned) {
jl_printf(JL_STDERR, "WARNING: failed to insert function stack unwind info: %lu\n", GetLastError());
warned = 1;
jl_profile_atomic([&]() {
if (!RtlAddFunctionTable(tbl, 1, (DWORD64)Section)) {
static int warned = 0;
if (!warned) {
jl_printf(JL_STDERR, "WARNING: failed to insert function stack unwind info: %lu\n", GetLastError());
warned = 1;
}
}
}
});
#endif
}
#endif
Expand Down Expand Up @@ -278,7 +308,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
Expand Down Expand Up @@ -404,20 +436,20 @@ class JuliaJITEventListener: public JITEventListener
codeinst = codeinst_it->second;
codeinst_in_flight.erase(codeinst_it);
}
uv_rwlock_wrlock(&threadsafe);
if (codeinst)
linfomap[Addr] = std::make_pair(Size, codeinst->def);
if (first) {
ObjectInfo tmp = {&debugObj,
(size_t)SectionSize,
(ptrdiff_t)(SectionAddr - SectionLoadAddr),
*Section,
nullptr,
};
objectmap[SectionLoadAddr] = tmp;
first = false;
}
uv_rwlock_wrunlock(&threadsafe);
jl_profile_atomic([&]() {
if (codeinst)
linfomap[Addr] = std::make_pair(Size, codeinst->def);
if (first) {
ObjectInfo tmp = {&debugObj,
(size_t)SectionSize,
(ptrdiff_t)(SectionAddr - SectionLoadAddr),
*Section,
nullptr,
};
objectmap[SectionLoadAddr] = tmp;
first = false;
}
});
}
jl_gc_safe_leave(ptls, gc_state);
}
Expand All @@ -431,14 +463,6 @@ class JuliaJITEventListener: public JITEventListener
uv_rwlock_rdlock(&threadsafe);
return objectmap;
}

Optional<std::map<size_t, ObjectInfo, revcomp>*> trygetObjectMap()
{
if (0 == uv_rwlock_tryrdlock(&threadsafe)) {
return &objectmap;
}
return {};
}
};

JL_DLLEXPORT void ORCNotifyObjectEmitted(JITEventListener *Listener,
Expand Down Expand Up @@ -482,7 +506,7 @@ static std::pair<char *, bool> 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;
Expand Down Expand Up @@ -722,7 +746,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();
}
Expand Down Expand Up @@ -873,7 +897,7 @@ static objfileentry_t &find_object_file(uint64_t fbase, StringRef fname) JL_NOTS
std::unique_ptr<MemoryBuffer> 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;

Expand Down Expand Up @@ -1292,28 +1316,33 @@ 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<char *>(Entry));
__register_frame(const_cast<char *>(Entry));
jl_profile_atomic([&]() {
libc_register_frame(const_cast<char *>(Entry));
__register_frame(const_cast<char *>(Entry));
});
});
}

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<char *>(Entry));
__deregister_frame(const_cast<char *>(Entry));
jl_profile_atomic([&]() {
libc_deregister_frame(const_cast<char *>(Entry));
__deregister_frame(const_cast<char *>(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 <type_traits>

struct unw_table_entry
Expand Down Expand Up @@ -1499,7 +1528,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.
Expand Down Expand Up @@ -1610,7 +1641,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<int32_t>((intptr_t)start_ips[i] - (intptr_t)start_ip);
}
Expand All @@ -1621,27 +1652,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)
Expand All @@ -1667,22 +1692,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<std::map<size_t, ObjectInfo, revcomp>*> maybeobjmap = jl_jit_events->trygetObjectMap();
if (maybeobjmap) {
std::map<size_t, ObjectInfo, revcomp> &objmap = **maybeobjmap;
std::map<size_t, ObjectInfo, revcomp>::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;
}

1 change: 0 additions & 1 deletion src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <dbghelp.h>
JL_DLLEXPORT EXCEPTION_DISPOSITION __julia_personality(
Expand Down
2 changes: 2 additions & 0 deletions src/signal-handling.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
39 changes: 35 additions & 4 deletions src/signals-mach.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@

#include "julia_assert.h"

// private keymgr stuff
#define KEYMGR_GCC3_DW2_OBJ_LIST 302
enum {
NM_ALLOW_RECURSION = 1,
NM_RECURSION_ILLEGAL = 2
};
extern void _keymgr_set_and_unlock_processwide_ptr(unsigned int key, void *ptr);
extern int _keymgr_unlock_processwide_ptr(unsigned int key);
extern void *_keymgr_get_and_lock_processwide_ptr(unsigned int key);
extern int _keymgr_get_and_lock_processwide_ptr_2(unsigned int key, void **result);
extern int _keymgr_set_lockmode_processwide_ptr(unsigned int key, unsigned int mode);

static void attach_exception_port(thread_port_t thread, int segv_only);

// low 16 bits are the thread id, the next 8 bits are the original gc_state
Expand Down Expand Up @@ -78,6 +90,17 @@ void *mach_segv_listener(void *arg)

static void allocate_segv_handler()
{
// ensure KEYMGR_GCC3_DW2_OBJ_LIST is initialized, as this requires malloc
// and thus can deadlock when used without first initializing it.
// Apple caused this problem in their libunwind in 10.9 (circa keymgr-28)
// when they removed this part of the code from keymgr.
// Much thanks to Apple for providing source code, or this would probably
// have simply remained unsolved forever on their platform.
// This is similar to just calling checkKeyMgrRegisteredFDEs
// (this is quite thread-unsafe)
if (_keymgr_set_lockmode_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, NM_ALLOW_RECURSION))
jl_error("_keymgr_set_lockmode_processwide_ptr failed");

arraylist_new(&suspended_threads, jl_n_threads);
pthread_t thread;
pthread_attr_t attr;
Expand Down Expand Up @@ -462,6 +485,9 @@ 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();
void *unused = NULL;
int keymgr_locked = _keymgr_get_and_lock_processwide_ptr_2(KEYMGR_GCC3_DW2_OBJ_LIST, &unused) == 0;
for (i = jl_n_threads; i-- > 0; ) {
// if there is no space left, break early
if (bt_size_cur >= bt_size_max - 1)
Expand Down Expand Up @@ -506,14 +532,18 @@ void *mach_profile_listener(void *arg)

// Mark the end of this block with 0
bt_data_prof[bt_size_cur++].uintptr = 0;

// Reset the alarm
kern_return_t ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port);
HANDLE_MACH_ERROR("clock_alarm", ret)
}
// We're done! Resume the thread.
jl_thread_resume(i, 0);
}
if (keymgr_locked)
_keymgr_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST);
jl_unlock_profile();
if (running) {
// Reset the alarm
kern_return_t ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port);
HANDLE_MACH_ERROR("clock_alarm", ret)
}
}
}

Expand Down Expand Up @@ -547,6 +577,7 @@ JL_DLLEXPORT int jl_profile_start_timer(void)
timerprof.tv_nsec = nsecprof%GIGA;

running = 1;
// ensure the alarm is running
ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port);
HANDLE_MACH_ERROR("clock_alarm", ret);

Expand Down
Loading