Skip to content

Commit 85c87a0

Browse files
committed
make Profile more thread/signal-safe
Fixes #35117 Fixes #13294
1 parent bf32ea4 commit 85c87a0

File tree

8 files changed

+148
-98
lines changed

8 files changed

+148
-98
lines changed

base/process.jl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ Returns successfully if the process has already exited, but throws an
546546
error if killing the process failed for other reasons (e.g. insufficient
547547
permissions).
548548
"""
549-
function kill(p::Process, signum::Integer)
549+
function kill(p::Process, signum::Integer=SIGTERM)
550550
iolock_begin()
551551
if process_running(p)
552552
@assert p.handle != C_NULL
@@ -558,9 +558,8 @@ function kill(p::Process, signum::Integer)
558558
iolock_end()
559559
nothing
560560
end
561-
kill(ps::Vector{Process}) = foreach(kill, ps)
562-
kill(ps::ProcessChain) = foreach(kill, ps.processes)
563-
kill(p::Process) = kill(p, SIGTERM)
561+
kill(ps::Vector{Process}, signum::Integer=SIGTERM) = for p in ps; kill(p, signum); end
562+
kill(ps::ProcessChain, signum::Integer=SIGTERM) = kill(ps.processes, signum)
564563

565564
"""
566565
getpid(process) -> Int32

src/debuginfo.cpp

Lines changed: 83 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323

2424
using namespace llvm;
2525

26-
using llvm_file_magic = file_magic;
27-
2826
#include "julia.h"
2927
#include "julia_internal.h"
3028
#include "debuginfo.h"
@@ -52,11 +50,41 @@ typedef object::SymbolRef SymRef;
5250
// and cannot have any interaction with the julia runtime
5351
static uv_rwlock_t threadsafe;
5452

55-
extern "C" void jl_init_debuginfo()
53+
extern "C" void jl_init_debuginfo(void)
5654
{
5755
uv_rwlock_init(&threadsafe);
5856
}
5957

58+
extern "C" void jl_lock_profile(void)
59+
{
60+
uv_rwlock_rdlock(&threadsafe);
61+
}
62+
63+
extern "C" void jl_unlock_profile(void)
64+
{
65+
uv_rwlock_rdunlock(&threadsafe);
66+
}
67+
68+
// some actions aren't signal (especially profiler) safe so we acquire a lock
69+
// around them to establish a mutual exclusion with unwinding from a signal
70+
template <typename T>
71+
static void jl_profile_atomic(T f)
72+
{
73+
uv_rwlock_wrlock(&threadsafe);
74+
#ifndef _OS_WINDOWS_
75+
sigset_t sset;
76+
sigset_t oset;
77+
sigfillset(&sset);
78+
pthread_sigmask(SIG_BLOCK, &sset, &oset);
79+
#endif
80+
f();
81+
#ifndef _OS_WINDOWS_
82+
pthread_sigmask(SIG_SETMASK, &oset, NULL);
83+
#endif
84+
uv_rwlock_wrunlock(&threadsafe);
85+
}
86+
87+
6088
// --- storing and accessing source location metadata ---
6189

6290
struct ObjectInfo {
@@ -131,13 +159,15 @@ static void create_PRUNTIME_FUNCTION(uint8_t *Code, size_t Size, StringRef fnnam
131159
JL_UNLOCK_NOGC(&jl_in_stackwalk);
132160
}
133161
#if defined(_CPU_X86_64_)
134-
if (!RtlAddFunctionTable(tbl, 1, (DWORD64)Section)) {
135-
static int warned = 0;
136-
if (!warned) {
137-
jl_printf(JL_STDERR, "WARNING: failed to insert function stack unwind info: %lu\n", GetLastError());
138-
warned = 1;
162+
jl_profile_atomic([&]() {
163+
if (!RtlAddFunctionTable(tbl, 1, (DWORD64)Section)) {
164+
static int warned = 0;
165+
if (!warned) {
166+
jl_printf(JL_STDERR, "WARNING: failed to insert function stack unwind info: %lu\n", GetLastError());
167+
warned = 1;
168+
}
139169
}
140-
}
170+
});
141171
#endif
142172
}
143173
#endif
@@ -278,7 +308,9 @@ class JuliaJITEventListener: public JITEventListener
278308
di->u.rti.name_ptr = 0;
279309
di->u.rti.table_data = arm_exidx_addr;
280310
di->u.rti.table_len = arm_exidx_len;
281-
_U_dyn_register(di);
311+
jl_profile_atomic([&]() {
312+
_U_dyn_register(di);
313+
});
282314
break;
283315
}
284316
#endif
@@ -404,20 +436,20 @@ class JuliaJITEventListener: public JITEventListener
404436
codeinst = codeinst_it->second;
405437
codeinst_in_flight.erase(codeinst_it);
406438
}
407-
uv_rwlock_wrlock(&threadsafe);
408-
if (codeinst)
409-
linfomap[Addr] = std::make_pair(Size, codeinst->def);
410-
if (first) {
411-
ObjectInfo tmp = {&debugObj,
412-
(size_t)SectionSize,
413-
(ptrdiff_t)(SectionAddr - SectionLoadAddr),
414-
*Section,
415-
nullptr,
416-
};
417-
objectmap[SectionLoadAddr] = tmp;
418-
first = false;
419-
}
420-
uv_rwlock_wrunlock(&threadsafe);
439+
jl_profile_atomic([&]() {
440+
if (codeinst)
441+
linfomap[Addr] = std::make_pair(Size, codeinst->def);
442+
if (first) {
443+
ObjectInfo tmp = {&debugObj,
444+
(size_t)SectionSize,
445+
(ptrdiff_t)(SectionAddr - SectionLoadAddr),
446+
*Section,
447+
nullptr,
448+
};
449+
objectmap[SectionLoadAddr] = tmp;
450+
first = false;
451+
}
452+
});
421453
}
422454
jl_gc_safe_leave(ptls, gc_state);
423455
}
@@ -431,14 +463,6 @@ class JuliaJITEventListener: public JITEventListener
431463
uv_rwlock_rdlock(&threadsafe);
432464
return objectmap;
433465
}
434-
435-
Optional<std::map<size_t, ObjectInfo, revcomp>*> trygetObjectMap()
436-
{
437-
if (0 == uv_rwlock_tryrdlock(&threadsafe)) {
438-
return &objectmap;
439-
}
440-
return {};
441-
}
442466
};
443467

444468
JL_DLLEXPORT void ORCNotifyObjectEmitted(JITEventListener *Listener,
@@ -482,7 +506,7 @@ static std::pair<char *, bool> jl_demangle(const char *name) JL_NOTSAFEPOINT
482506
}
483507

484508
static JuliaJITEventListener *jl_jit_events;
485-
JITEventListener *CreateJuliaJITEventListener()
509+
JITEventListener *CreateJuliaJITEventListener(void)
486510
{
487511
jl_jit_events = new JuliaJITEventListener();
488512
return jl_jit_events;
@@ -722,7 +746,7 @@ openDebugInfo(StringRef debuginfopath, const debug_link_info &info)
722746

723747
auto error_splitobj = object::ObjectFile::createObjectFile(
724748
SplitFile.get().get()->getMemBufferRef(),
725-
llvm_file_magic::unknown);
749+
file_magic::unknown);
726750
if (!error_splitobj) {
727751
return error_splitobj.takeError();
728752
}
@@ -873,7 +897,7 @@ static objfileentry_t &find_object_file(uint64_t fbase, StringRef fname) JL_NOTS
873897
std::unique_ptr<MemoryBuffer> membuf = MemoryBuffer::getMemBuffer(
874898
StringRef((const char *)fbase, msize), "", false);
875899
auto origerrorobj = llvm::object::ObjectFile::createObjectFile(
876-
membuf->getMemBufferRef(), llvm_file_magic::unknown);
900+
membuf->getMemBufferRef(), file_magic::unknown);
877901
if (!origerrorobj)
878902
return entry;
879903

@@ -1292,28 +1316,33 @@ void register_eh_frames(uint8_t *Addr, size_t Size)
12921316
// See http://lists.cs.uiuc.edu/pipermail/llvmdev/2013-April/061768.html
12931317
processFDEs((char*)Addr, Size, [](const char *Entry) {
12941318
if (!libc_register_frame) {
1295-
libc_register_frame = (void(*)(void*))dlsym(RTLD_NEXT,"__register_frame");
1319+
libc_register_frame = (void(*)(void*))dlsym(RTLD_NEXT, "__register_frame");
12961320
}
12971321
assert(libc_register_frame);
1298-
libc_register_frame(const_cast<char *>(Entry));
1299-
__register_frame(const_cast<char *>(Entry));
1322+
jl_profile_atomic([&]() {
1323+
libc_register_frame(const_cast<char *>(Entry));
1324+
__register_frame(const_cast<char *>(Entry));
1325+
});
13001326
});
13011327
}
13021328

13031329
void deregister_eh_frames(uint8_t *Addr, size_t Size)
13041330
{
13051331
processFDEs((char*)Addr, Size, [](const char *Entry) {
13061332
if (!libc_deregister_frame) {
1307-
libc_deregister_frame = (void(*)(void*))dlsym(RTLD_NEXT,"__deregister_frame");
1333+
libc_deregister_frame = (void(*)(void*))dlsym(RTLD_NEXT, "__deregister_frame");
13081334
}
13091335
assert(libc_deregister_frame);
1310-
libc_deregister_frame(const_cast<char *>(Entry));
1311-
__deregister_frame(const_cast<char *>(Entry));
1336+
jl_profile_atomic([&]() {
1337+
libc_deregister_frame(const_cast<char *>(Entry));
1338+
__deregister_frame(const_cast<char *>(Entry));
1339+
});
13121340
});
13131341
}
13141342

13151343
#elif defined(_OS_LINUX_) && \
1316-
defined(JL_UNW_HAS_FORMAT_IP) && !defined(_CPU_ARM_)
1344+
defined(JL_UNW_HAS_FORMAT_IP) && \
1345+
!defined(_CPU_ARM_) // ARM does not have/use .eh_frame, so we handle this elsewhere
13171346
#include <type_traits>
13181347

13191348
struct unw_table_entry
@@ -1499,7 +1528,9 @@ static DW_EH_PE parseCIE(const uint8_t *Addr, const uint8_t *End)
14991528
void register_eh_frames(uint8_t *Addr, size_t Size)
15001529
{
15011530
// System unwinder
1502-
__register_frame(Addr);
1531+
jl_profile_atomic([&]() {
1532+
__register_frame(Addr);
1533+
});
15031534
// Our unwinder
15041535
unw_dyn_info_t *di = new unw_dyn_info_t;
15051536
// In a shared library, this is set to the address of the PLT.
@@ -1610,7 +1641,7 @@ void register_eh_frames(uint8_t *Addr, size_t Size)
16101641
start_ips[cur_entry] = start;
16111642
cur_entry++;
16121643
});
1613-
for (size_t i = 0;i < nentries;i++) {
1644+
for (size_t i = 0; i < nentries; i++) {
16141645
table[i].start_ip_offset =
16151646
safe_trunc<int32_t>((intptr_t)start_ips[i] - (intptr_t)start_ip);
16161647
}
@@ -1621,27 +1652,21 @@ void register_eh_frames(uint8_t *Addr, size_t Size)
16211652
di->start_ip = start_ip;
16221653
di->end_ip = end_ip;
16231654

1624-
_U_dyn_register(di);
1655+
jl_profile_atomic([&]() {
1656+
_U_dyn_register(di);
1657+
});
16251658
}
16261659

16271660
void deregister_eh_frames(uint8_t *Addr, size_t Size)
16281661
{
1629-
__deregister_frame(Addr);
1630-
// Deregistering with our unwinder requires a lookup table to find the
1631-
// the allocated entry above (or we could look in libunwind's internal
1662+
jl_profile_atomic([&]() {
1663+
__deregister_frame(Addr);
1664+
});
1665+
// Deregistering with our unwinder (_U_dyn_cancel) requires a lookup table
1666+
// to find the allocated entry above (or looking into libunwind's internal
16321667
// data structures).
16331668
}
16341669

1635-
#elif defined(_CPU_ARM_)
1636-
1637-
void register_eh_frames(uint8_t *Addr, size_t Size)
1638-
{
1639-
}
1640-
1641-
void deregister_eh_frames(uint8_t *Addr, size_t Size)
1642-
{
1643-
}
1644-
16451670
#else
16461671

16471672
void register_eh_frames(uint8_t *Addr, size_t Size)
@@ -1667,22 +1692,3 @@ uint64_t jl_getUnwindInfo(uint64_t dwAddr)
16671692
uv_rwlock_rdunlock(&threadsafe);
16681693
return ipstart;
16691694
}
1670-
1671-
extern "C"
1672-
uint64_t jl_trygetUnwindInfo(uint64_t dwAddr)
1673-
{
1674-
// Might be called from unmanaged thread
1675-
Optional<std::map<size_t, ObjectInfo, revcomp>*> maybeobjmap = jl_jit_events->trygetObjectMap();
1676-
if (maybeobjmap) {
1677-
std::map<size_t, ObjectInfo, revcomp> &objmap = **maybeobjmap;
1678-
std::map<size_t, ObjectInfo, revcomp>::iterator it = objmap.lower_bound(dwAddr);
1679-
uint64_t ipstart = 0; // ip of the start of the section (if found)
1680-
if (it != objmap.end() && dwAddr < it->first + it->second.SectionSize) {
1681-
ipstart = (uint64_t)(uintptr_t)(*it).first;
1682-
}
1683-
uv_rwlock_rdunlock(&threadsafe);
1684-
return ipstart;
1685-
}
1686-
return 0;
1687-
}
1688-

src/julia_internal.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,6 @@ typedef struct {
756756

757757
// Might be called from unmanaged thread
758758
uint64_t jl_getUnwindInfo(uint64_t dwBase);
759-
uint64_t jl_trygetUnwindInfo(uint64_t dwBase);
760759
#ifdef _OS_WINDOWS_
761760
#include <dbghelp.h>
762761
JL_DLLEXPORT EXCEPTION_DISPOSITION __julia_personality(

src/signal-handling.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ static const uint64_t GIGA = 1000000000ULL;
2727
// Timers to take samples at intervals
2828
JL_DLLEXPORT void jl_profile_stop_timer(void);
2929
JL_DLLEXPORT int jl_profile_start_timer(void);
30+
void jl_lock_profile(void);
31+
void jl_unlock_profile(void);
3032

3133
static uint64_t jl_last_sigint_trigger = 0;
3234
static uint64_t jl_disable_sigint_time = 0;

src/signals-mach.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ void *mach_profile_listener(void *arg)
462462
HANDLE_MACH_ERROR("mach_msg", ret);
463463
// sample each thread, round-robin style in reverse order
464464
// (so that thread zero gets notified last)
465+
jl_lock_profile();
465466
for (i = jl_n_threads; i-- > 0; ) {
466467
// if there is no space left, break early
467468
if (bt_size_cur >= bt_size_max - 1)
@@ -506,14 +507,16 @@ void *mach_profile_listener(void *arg)
506507

507508
// Mark the end of this block with 0
508509
bt_data_prof[bt_size_cur++].uintptr = 0;
509-
510-
// Reset the alarm
511-
kern_return_t ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port);
512-
HANDLE_MACH_ERROR("clock_alarm", ret)
513510
}
514511
// We're done! Resume the thread.
515512
jl_thread_resume(i, 0);
516513
}
514+
jl_unlock_profile();
515+
if (running) {
516+
// Reset the alarm
517+
kern_return_t ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port);
518+
HANDLE_MACH_ERROR("clock_alarm", ret)
519+
}
517520
}
518521
}
519522

@@ -547,6 +550,7 @@ JL_DLLEXPORT int jl_profile_start_timer(void)
547550
timerprof.tv_nsec = nsecprof%GIGA;
548551

549552
running = 1;
553+
// ensure the alarm is running
550554
ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port);
551555
HANDLE_MACH_ERROR("clock_alarm", ret);
552556

src/signals-unix.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,8 @@ static void *signal_listener(void *arg)
673673
unw_context_t *signal_context;
674674
// sample each thread, round-robin style in reverse order
675675
// (so that thread zero gets notified last)
676+
if (critical || profile)
677+
jl_lock_profile();
676678
for (int i = jl_n_threads; i-- > 0; ) {
677679
// notify thread to stop
678680
jl_thread_suspend_and_get_state(i, &signal_context);
@@ -717,6 +719,8 @@ static void *signal_listener(void *arg)
717719
// notify thread to resume
718720
jl_thread_resume(i, sig);
719721
}
722+
if (critical || profile)
723+
jl_unlock_profile();
720724
#endif
721725

722726
// this part is async with the running of the rest of the program

0 commit comments

Comments
 (0)