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

profile: fix async deadlock #44781

Merged
merged 1 commit into from
Mar 29, 2022
Merged
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
48 changes: 32 additions & 16 deletions src/debuginfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,29 +47,43 @@ typedef object::SymbolRef SymRef;
// while holding this lock.
// Certain functions in this file might be called from an unmanaged thread
// and cannot have any interaction with the julia runtime
static uv_rwlock_t threadsafe;
// They also may be re-entrant, and operating while threads are paused, so we
// separately manage the re-entrant count behavior for safety across platforms
// Note that we cannot safely upgrade read->write
static uv_rwlock_t debuginfo_asyncsafe;
static pthread_key_t debuginfo_asyncsafe_held;

void jl_init_debuginfo(void)
{
uv_rwlock_init(&threadsafe);
uv_rwlock_init(&debuginfo_asyncsafe);
if (pthread_key_create(&debuginfo_asyncsafe_held, NULL))
jl_error("fatal: pthread_key_create failed");
}

extern "C" JL_DLLEXPORT void jl_lock_profile_impl(void)
{
uv_rwlock_rdlock(&threadsafe);
uintptr_t held = (uintptr_t)pthread_getspecific(debuginfo_asyncsafe_held);
if (held++ == 0)
uv_rwlock_rdlock(&debuginfo_asyncsafe);
pthread_setspecific(debuginfo_asyncsafe_held, (void*)held);
}

extern "C" JL_DLLEXPORT void jl_unlock_profile_impl(void)
{
uv_rwlock_rdunlock(&threadsafe);
uintptr_t held = (uintptr_t)pthread_getspecific(debuginfo_asyncsafe_held);
assert(held);
if (--held == 0)
uv_rwlock_rdunlock(&debuginfo_asyncsafe);
pthread_setspecific(debuginfo_asyncsafe_held, (void*)held);
}

// 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);
assert(0 == (uintptr_t)pthread_getspecific(debuginfo_asyncsafe_held));
uv_rwlock_wrlock(&debuginfo_asyncsafe);
#ifndef _OS_WINDOWS_
sigset_t sset;
sigset_t oset;
Expand All @@ -80,7 +94,7 @@ static void jl_profile_atomic(T f)
#ifndef _OS_WINDOWS_
pthread_sigmask(SIG_SETMASK, &oset, NULL);
#endif
uv_rwlock_wrunlock(&threadsafe);
uv_rwlock_wrunlock(&debuginfo_asyncsafe);
}


Expand Down Expand Up @@ -188,12 +202,12 @@ class JITObjectRegistry
public:
jl_method_instance_t *lookupLinfo(size_t pointer) JL_NOTSAFEPOINT
{
uv_rwlock_rdlock(&threadsafe);
jl_lock_profile_impl();
auto region = linfomap.lower_bound(pointer);
jl_method_instance_t *linfo = NULL;
if (region != linfomap.end() && pointer < region->first + region->second.first)
linfo = region->second.second;
uv_rwlock_rdunlock(&threadsafe);
jl_unlock_profile_impl();
return linfo;
}

Expand Down Expand Up @@ -448,9 +462,10 @@ static int lookup_pointer(

// DWARFContext/DWARFUnit update some internal tables during these queries, so
// a lock is needed.
uv_rwlock_wrlock(&threadsafe);
assert(0 == (uintptr_t)pthread_getspecific(debuginfo_asyncsafe_held));
uv_rwlock_wrlock(&debuginfo_asyncsafe);
auto inlineInfo = context->getInliningInfoForAddress(makeAddress(Section, pointer + slide), infoSpec);
uv_rwlock_wrunlock(&threadsafe);
uv_rwlock_wrunlock(&debuginfo_asyncsafe);

int fromC = (*frames)[0].fromC;
int n_frames = inlineInfo.getNumberOfFrames();
Expand All @@ -473,9 +488,9 @@ static int lookup_pointer(
info = inlineInfo.getFrame(i);
}
else {
uv_rwlock_wrlock(&threadsafe);
uv_rwlock_wrlock(&debuginfo_asyncsafe);
info = context->getLineInfoForAddress(makeAddress(Section, pointer + slide), infoSpec);
uv_rwlock_wrunlock(&threadsafe);
uv_rwlock_wrunlock(&debuginfo_asyncsafe);
}

jl_frame_t *frame = &(*frames)[i];
Expand Down Expand Up @@ -1148,7 +1163,8 @@ int jl_DI_for_fptr(uint64_t fptr, uint64_t *symsize, int64_t *slide,
object::SectionRef *Section, llvm::DIContext **context) JL_NOTSAFEPOINT
{
int found = 0;
uv_rwlock_wrlock(&threadsafe);
assert(0 == (uintptr_t)pthread_getspecific(debuginfo_asyncsafe_held));
uv_rwlock_wrlock(&debuginfo_asyncsafe);
std::map<size_t, ObjectInfo, revcomp> &objmap = jl_jit_object_registry.getObjectMap();
std::map<size_t, ObjectInfo, revcomp>::iterator fit = objmap.lower_bound(fptr);

Expand All @@ -1164,7 +1180,7 @@ int jl_DI_for_fptr(uint64_t fptr, uint64_t *symsize, int64_t *slide,
}
found = 1;
}
uv_rwlock_wrunlock(&threadsafe);
uv_rwlock_wrunlock(&debuginfo_asyncsafe);
return found;
}

Expand Down Expand Up @@ -1613,13 +1629,13 @@ extern "C" JL_DLLEXPORT
uint64_t jl_getUnwindInfo_impl(uint64_t dwAddr)
{
// Might be called from unmanaged thread
uv_rwlock_rdlock(&threadsafe);
jl_lock_profile_impl();
std::map<size_t, ObjectInfo, revcomp> &objmap = jl_jit_object_registry.getObjectMap();
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);
jl_unlock_profile_impl();
return ipstart;
}