/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/sys.c
1 071 строка
32 KB
Ian Butterworth
Fix analyzegc on macOS (#62516)
29 июл 2026, 02:34
Не верифицирован
29 июл 2026, 02:34
87fc06b
Код
Авторство
О чём код?
// This file is a part of Julia. License is MIT: https://julialang.org/license /* sys.c I/O and operating system utility functions */ #include <sys/stat.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <signal.h> #include <fcntl.h> #include <stdio.h> #include "julia.h" #include "julia_internal.h" #ifdef _OS_WINDOWS_ #include <psapi.h> #else #include <unistd.h> #if !defined(_SC_NPROCESSORS_ONLN) || defined(_OS_FREEBSD_) || defined(_OS_DARWIN_) // try secondary location for _SC_NPROCESSORS_ONLN, or for HW_AVAILCPU on BSDs #include <sys/sysctl.h> #endif #include <sys/wait.h> #include <sys/ptrace.h> #include <sys/mman.h> #include <dlfcn.h> #include <grp.h> // For `struct termios` #include <termios.h> #endif #ifndef _OS_WINDOWS_ // for getrusage #include <sys/types.h> #include <sys/time.h> #include <sys/resource.h> #endif #ifdef __APPLE__ #include <mach-o/dyld.h> #include <mach-o/nlist.h> #include <sys/types.h> // for jl_raise_debugger #elif !defined(_OS_WINDOWS_) #include <link.h> #endif #ifdef __SSE__ #include <xmmintrin.h> #endif #ifdef _COMPILER_MSAN_ENABLED_ #include <sanitizer/msan_interface.h> #endif #include "julia_assert.h" #ifdef __cplusplus extern "C" { #endif JL_DLLEXPORT int jl_sizeof_off_t(void) { return sizeof(off_t); } #ifndef _OS_WINDOWS_ JL_DLLEXPORT int jl_sizeof_mode_t(void) { return sizeof(mode_t); } JL_DLLEXPORT int jl_ftruncate(int fd, int64_t length) { return ftruncate(fd, (off_t)length); } JL_DLLEXPORT int64_t jl_lseek(int fd, int64_t offset, int whence) { return lseek(fd, (off_t)offset, whence); } JL_DLLEXPORT ssize_t jl_pwrite(int fd, const void *buf, size_t count, int64_t offset) { return pwrite(fd, buf, count, (off_t)offset); } JL_DLLEXPORT void *jl_mmap(void *addr, size_t length, int prot, int flags, int fd, int64_t offset) { return mmap(addr, length, prot, flags, fd, (off_t)offset); } #else JL_DLLEXPORT int64_t jl_lseek(HANDLE fd, int64_t offset, int whence) { LARGE_INTEGER tell; tell.QuadPart = offset; if (SetFilePointerEx(fd, tell, &tell, whence) == 0) return -1; return tell.QuadPart; } #endif JL_DLLEXPORT int jl_sizeof_ios_t(void) { return sizeof(ios_t); } JL_DLLEXPORT long jl_ios_fd(ios_t *s) { return s->fd; } JL_DLLEXPORT int32_t jl_nb_available(ios_t *s) { return (int32_t)(s->size - s->bpos); } // --- dir/file stuff --- JL_DLLEXPORT char *jl_uv_fs_t_ptr(uv_fs_t *req) { return (char*)req->ptr; } JL_DLLEXPORT char *jl_uv_fs_t_path(uv_fs_t *req) { return (char*)req->path; } // --- stat --- JL_DLLEXPORT int jl_sizeof_stat(void) { return sizeof(uv_stat_t); } JL_DLLEXPORT int32_t jl_stat(const char *path, char *statbuf) JL_NOTSAFEPOINT { uv_fs_t req; int ret; // Ideally one would use the statbuf for the storage in req, but // it's not clear that this is possible using libuv ret = uv_fs_stat(unused_uv_loop_arg, &req, path, NULL); if (ret == 0) memcpy(statbuf, req.ptr, sizeof(uv_stat_t)); uv_fs_req_cleanup(&req); return ret; } JL_DLLEXPORT int32_t jl_lstat(const char *path, char *statbuf) { uv_fs_t req; int ret; ret = uv_fs_lstat(unused_uv_loop_arg, &req, path, NULL); if (ret == 0) memcpy(statbuf, req.ptr, sizeof(uv_stat_t)); uv_fs_req_cleanup(&req); return ret; } JL_DLLEXPORT int32_t jl_fstat(uv_os_fd_t fd, char *statbuf) { uv_fs_t req; int ret; ret = uv_fs_fstat(unused_uv_loop_arg, &req, fd, NULL); if (ret == 0) memcpy(statbuf, req.ptr, sizeof(uv_stat_t)); uv_fs_req_cleanup(&req); return ret; } JL_DLLEXPORT unsigned int jl_stat_dev(char *statbuf) { return ((uv_stat_t*)statbuf)->st_dev; } JL_DLLEXPORT unsigned int jl_stat_ino(char *statbuf) { return ((uv_stat_t*)statbuf)->st_ino; } JL_DLLEXPORT unsigned int jl_stat_mode(char *statbuf) { return ((uv_stat_t*)statbuf)->st_mode; } JL_DLLEXPORT unsigned int jl_stat_nlink(char *statbuf) { return ((uv_stat_t*)statbuf)->st_nlink; } JL_DLLEXPORT unsigned int jl_stat_uid(char *statbuf) { return ((uv_stat_t*)statbuf)->st_uid; } JL_DLLEXPORT unsigned int jl_stat_gid(char *statbuf) { return ((uv_stat_t*)statbuf)->st_gid; } JL_DLLEXPORT unsigned int jl_stat_rdev(char *statbuf) { return ((uv_stat_t*)statbuf)->st_rdev; } JL_DLLEXPORT uint64_t jl_stat_size(char *statbuf) { return ((uv_stat_t*)statbuf)->st_size; } JL_DLLEXPORT uint64_t jl_stat_blksize(char *statbuf) { return ((uv_stat_t*)statbuf)->st_blksize; } JL_DLLEXPORT uint64_t jl_stat_blocks(char *statbuf) { return ((uv_stat_t*)statbuf)->st_blocks; } /* // atime is stupid, let's not support it JL_DLLEXPORT double jl_stat_atime(char *statbuf) { uv_stat_t *s; s = (uv_stat_t*)statbuf; return (double)s->st_atim.tv_sec + (double)s->st_atim.tv_nsec * 1e-9; } */ JL_DLLEXPORT double jl_stat_mtime(char *statbuf) { uv_stat_t *s; s = (uv_stat_t*)statbuf; return (double)s->st_mtim.tv_sec + (double)s->st_mtim.tv_nsec * 1e-9; } JL_DLLEXPORT double jl_stat_ctime(char *statbuf) { uv_stat_t *s; s = (uv_stat_t*)statbuf; return (double)s->st_ctim.tv_sec + (double)s->st_ctim.tv_nsec * 1e-9; } JL_DLLEXPORT unsigned long jl_getuid(void) { #ifdef _OS_WINDOWS_ return -1; #else return getuid(); #endif } JL_DLLEXPORT unsigned long jl_geteuid(void) { #ifdef _OS_WINDOWS_ return -1; #else return geteuid(); #endif } // --- buffer manipulation --- JL_DLLEXPORT jl_array_t *jl_take_buffer(ios_t *s) { size_t n; jl_array_t *a; if (s->buf == &s->local[0]) { // small data case. copies, but this can be avoided using the // technique of jl_readuntil below. a = jl_pchar_to_array(s->buf, s->size); ios_trunc(s, 0); } else { char *b = ios_take_buffer(s, &n); a = jl_ptr_to_array_1d(jl_array_uint8_type, b, n-1, 1); } return a; } // str: if 1 return a string, otherwise return a Vector{UInt8} // chomp: // 0 - keep delimiter // 1 - remove 1 byte delimiter // 2 - remove 2 bytes \r\n if present JL_DLLEXPORT jl_value_t *jl_readuntil(ios_t *s, uint8_t delim, uint8_t str, uint8_t chomp) JL_CANSAFEPOINT { jl_array_t *a; // manually inlined common case char *pd = (char*)memchr(s->buf + s->bpos, delim, (size_t)(s->size - s->bpos)); if (pd) { size_t n = pd - (s->buf + s->bpos) + 1; size_t nchomp = 0; if (chomp) { nchomp = chomp == 2 ? ios_nchomp(s, n) : 1; } if (str) { jl_value_t *str = jl_pchar_to_string(s->buf + s->bpos, n - nchomp); s->bpos += n; return str; } a = jl_alloc_array_1d(jl_array_uint8_type, n - nchomp); memcpy(jl_array_data(a, uint8_t), s->buf + s->bpos, n - nchomp); s->bpos += n; } else { a = jl_alloc_array_1d(jl_array_uint8_type, 80); ios_t dest; ios_mem(&dest, 0); char *mem = jl_array_data(a, char); ios_setbuf(&dest, (char*)mem, 80, 0); size_t n = ios_copyuntil(&dest, s, delim, 1); if (chomp && n > 0 && dest.buf[n - 1] == delim) { n--; if (chomp == 2 && n > 0 && dest.buf[n - 1] == '\r') { n--; } int truncret = ios_trunc(&dest, n); // it should always be possible to truncate dest assert(truncret == 0); (void)truncret; // ensure the variable is used to avoid warnings } if (dest.buf != mem) { a = jl_take_buffer(&dest); } else { a->dimsize[0] = n; } if (str) { JL_GC_PUSH1(&a); jl_value_t *st = jl_array_to_string(a); JL_GC_POP(); return st; } } return (jl_value_t*)a; } // read up to buflen bytes, including delim, into buf. returns number of bytes read. JL_DLLEXPORT size_t jl_readuntil_buf(ios_t *s, uint8_t delim, uint8_t *buf, size_t buflen) { // manually inlined common case size_t avail = (size_t)(s->size - s->bpos); if (avail > buflen) avail = buflen; char *pd = (char*)memchr(s->buf + s->bpos, delim, avail); if (pd) { size_t n = pd - (s->buf + s->bpos) + 1; memcpy(buf, s->buf + s->bpos, n); s->bpos += n; return n; } else { size_t total = avail; memcpy(buf, s->buf + s->bpos, avail); s->bpos += avail; if (avail == buflen) return total; // code derived from ios_copyuntil while (!ios_eof(s)) { avail = ios_readprep(s, 160); // read LINE_CHUNK_SIZE if (avail == 0) break; if (total+avail > buflen) avail = buflen-total; char *pd = (char*)memchr(s->buf+s->bpos, delim, avail); if (pd == NULL) { memcpy(buf+total, s->buf+s->bpos, avail); s->bpos += avail; total += avail; if (buflen == total) return total; } else { size_t ntowrite = pd - (s->buf+s->bpos) + 1; memcpy(buf+total, s->buf+s->bpos, ntowrite); s->bpos += ntowrite; total += ntowrite; return total; } } s->_eof = 1; return total; } } JL_DLLEXPORT int jl_ios_buffer_n(ios_t *s, const size_t n) { size_t space, ret; do { space = (size_t)(s->size - s->bpos); ret = ios_readprep(s, n); if (space == ret && ret < n) return 1; } while (ret < n); return 0; } JL_DLLEXPORT uint64_t jl_ios_get_nbyte_int(ios_t *s, const size_t n) { assert(n <= 8); uint64_t x = 0; uint8_t *buf = (uint8_t*)&s->buf[s->bpos]; if (n == 8) { // expecting loop unrolling optimization for (size_t i = 0; i < 8; i++) x |= (uint64_t)buf[i] << (i << 3); } else if (n >= 4) { // expecting loop unrolling optimization for (size_t i = 0; i < 4; i++) x |= (uint64_t)buf[i] << (i << 3); for (size_t i = 4; i < n; i++) x |= (uint64_t)buf[i] << (i << 3); } else { for (size_t i = 0; i < n; i++) x |= (uint64_t)buf[i] << (i << 3); } s->bpos += n; return x; } // -- syscall utilities -- JL_DLLEXPORT int jl_errno(void) JL_NOTSAFEPOINT { return errno; } JL_DLLEXPORT void jl_set_errno(int e) JL_NOTSAFEPOINT { errno = e; } // -- get the number of CPU threads (logical cores) -- #ifdef _OS_WINDOWS_ typedef DWORD (WINAPI *GAPC)(WORD); #ifndef ALL_PROCESSOR_GROUPS #define ALL_PROCESSOR_GROUPS 0xffff #endif #endif // Apple's M1 processor is a big.LITTLE style processor, with 4x "performance" // cores, and 4x "efficiency" cores. Because Julia expects to be able to run // things like heavy linear algebra workloads on all cores, it's best for us // to only spawn as many threads as there are performance cores. Once macOS // 12 is released, we'll be able to query the multiple "perf levels" of the // cores of a CPU (see this PR [0] to pytorch/cpuinfo for an example) but // until it's released, we will just recognize the M1 by its CPU family // identifier, then subtract how many efficiency cores we know it has. JL_DLLEXPORT int jl_cpu_threads(void) JL_NOTSAFEPOINT { #if defined(HW_AVAILCPU) && defined(HW_NCPU) size_t len = 4; int32_t count; int nm[2] = {CTL_HW, HW_AVAILCPU}; sysctl(nm, 2, &count, &len, NULL, 0); if (count < 1) { nm[1] = HW_NCPU; sysctl(nm, 2, &count, &len, NULL, 0); if (count < 1) { count = 1; } } #if defined(__APPLE__) && defined(_CPU_AARCH64_) //MacOS 12 added a way to query performance cores char buf[7]; len = 7; sysctlbyname("kern.osrelease", buf, &len, NULL, 0); if (buf[0] > 1 && buf[1] > 0){ len = 4; sysctlbyname("hw.perflevel0.physicalcpu", &count, &len, NULL, 0); } else { int32_t family = 0; len = 4; sysctlbyname("hw.cpufamily", &family, &len, NULL, 0); if (family >= 1 && count > 1) { if (family == CPUFAMILY_ARM_FIRESTORM_ICESTORM) { // We know the Apple M1 has 4 efficiency cores, so subtract them out. count -= 4; } } } #endif return count; #elif defined(_SC_NPROCESSORS_ONLN) long count = sysconf(_SC_NPROCESSORS_ONLN); if (count < 1) return 1; return count; #elif defined(_OS_WINDOWS_) //Try to get WIN7 API method GAPC gapc; if (jl_dlsym(jl_kernel32_handle, "GetActiveProcessorCount", (void **)&gapc, 0, 0)) { return gapc(ALL_PROCESSOR_GROUPS); } else { //fall back on GetSystemInfo SYSTEM_INFO info; GetSystemInfo(&info); return info.dwNumberOfProcessors; } #else #warning "cpu core detection not defined for this platform" return 1; #endif } JL_DLLEXPORT int jl_effective_threads(void) JL_NOTSAFEPOINT { // We want the more conservative estimate of the two. int cpu_threads = jl_cpu_threads(); int available_parallelism = uv_available_parallelism(); return available_parallelism < cpu_threads ? available_parallelism : cpu_threads; } // -- precompile jobserver -- // A named-semaphore token pool created by the orchestrating precompile process // and shared with worker subprocesses to bound the total number of AOT codegen // threads across all parallel workers (see JuliaLang/julia#58591). Workers open // it by name (via the JULIA_PRECOMPILE_JOBSERVER env var); the client side lives // in aotcompile.cpp. // // All state is mutex-guarded: acquire/release run on arbitrary task threads and // may race with destroy on exception paths. At most one jobserver is active per // process; create returns NULL while one exists, so a concurrent session joins // the existing pool (see jl_precompile_jobserver_active) rather than creating // its own. static uv_once_t jl_precompile_jobserver_once = UV_ONCE_INIT; static uv_mutex_t jl_precompile_jobserver_lock; static unsigned jl_precompile_jobserver_counter = 0; static char jl_precompile_jobserver_name[64]; #ifdef _OS_WINDOWS_ static HANDLE jl_precompile_jobserver_sem = NULL; #define JL_PRECOMPILE_JOBSERVER_ACTIVE() (jl_precompile_jobserver_sem != NULL) #else #include <semaphore.h> static sem_t *jl_precompile_jobserver_sem = SEM_FAILED; #define JL_PRECOMPILE_JOBSERVER_ACTIVE() (jl_precompile_jobserver_sem != SEM_FAILED) #endif static void jl_precompile_jobserver_init_lock(void) JL_NOTSAFEPOINT { uv_mutex_init(&jl_precompile_jobserver_lock); } // Create the jobserver with `ntokens` tokens. Returns its name on success (for // export to workers via the environment), or NULL on failure or when another // jobserver is already active in this process. The name carries a per-process // counter so workers left over from a previous session cannot open a newer // session's pool. JL_DLLEXPORT const char *jl_precompile_jobserver_create(int ntokens) JL_NOTSAFEPOINT { if (ntokens < 1) ntokens = 1; uv_once(&jl_precompile_jobserver_once, jl_precompile_jobserver_init_lock); uv_mutex_lock(&jl_precompile_jobserver_lock); const char *name = NULL; if (!JL_PRECOMPILE_JOBSERVER_ACTIVE()) { unsigned counter = ++jl_precompile_jobserver_counter; #ifdef _OS_WINDOWS_ snprintf(jl_precompile_jobserver_name, sizeof(jl_precompile_jobserver_name), "jl_pc_%lu_%u", (unsigned long)GetCurrentProcessId(), counter); jl_precompile_jobserver_sem = CreateSemaphoreA(NULL, ntokens, ntokens, jl_precompile_jobserver_name); #else // Name must start with '/' and stay short (macOS limits names to ~31 chars). snprintf(jl_precompile_jobserver_name, sizeof(jl_precompile_jobserver_name), "/jl_pc_%ld_%u", (long)getpid(), counter); sem_unlink(jl_precompile_jobserver_name); // clear any stale instance from a crashed run jl_precompile_jobserver_sem = sem_open(jl_precompile_jobserver_name, O_CREAT | O_EXCL, 0600, (unsigned)ntokens); #endif if (JL_PRECOMPILE_JOBSERVER_ACTIVE()) name = jl_precompile_jobserver_name; } uv_mutex_unlock(&jl_precompile_jobserver_lock); return name; } // Report whether this process owns an active precompile jobserver. Lets a // session whose own create failed tell "another session already owns the pool" // (join it) from "the OS refused the semaphore" (no pool to join). JL_DLLEXPORT int jl_precompile_jobserver_active(void) JL_NOTSAFEPOINT { uv_once(&jl_precompile_jobserver_once, jl_precompile_jobserver_init_lock); uv_mutex_lock(&jl_precompile_jobserver_lock); int active = JL_PRECOMPILE_JOBSERVER_ACTIVE(); uv_mutex_unlock(&jl_precompile_jobserver_lock); return active; } // Tear down the jobserver created by jl_precompile_jobserver_create. JL_DLLEXPORT void jl_precompile_jobserver_destroy(void) JL_NOTSAFEPOINT { uv_once(&jl_precompile_jobserver_once, jl_precompile_jobserver_init_lock); uv_mutex_lock(&jl_precompile_jobserver_lock); #ifdef _OS_WINDOWS_ if (jl_precompile_jobserver_sem != NULL) { CloseHandle(jl_precompile_jobserver_sem); jl_precompile_jobserver_sem = NULL; } #else if (jl_precompile_jobserver_sem != SEM_FAILED) { sem_close(jl_precompile_jobserver_sem); sem_unlink(jl_precompile_jobserver_name); jl_precompile_jobserver_sem = SEM_FAILED; } #endif uv_mutex_unlock(&jl_precompile_jobserver_lock); } // Take one token so the orchestrator can hold a baseline per running worker // against the shared pool. Non-blocking: returns 1 on success, 0 when no token // is available, and -1 when no jobserver is active (e.g. torn down) so pollers // stop waiting. The yielding poll loop lives on the Julia side. JL_DLLEXPORT int jl_precompile_jobserver_acquire(void) JL_NOTSAFEPOINT { uv_once(&jl_precompile_jobserver_once, jl_precompile_jobserver_init_lock); uv_mutex_lock(&jl_precompile_jobserver_lock); int ret; if (!JL_PRECOMPILE_JOBSERVER_ACTIVE()) ret = -1; #ifdef _OS_WINDOWS_ else ret = WaitForSingleObject(jl_precompile_jobserver_sem, 0) == WAIT_OBJECT_0; #else else ret = sem_trywait(jl_precompile_jobserver_sem) == 0; #endif uv_mutex_unlock(&jl_precompile_jobserver_lock); return ret; } // Return one token previously taken with jl_precompile_jobserver_acquire. JL_DLLEXPORT void jl_precompile_jobserver_release(void) JL_NOTSAFEPOINT { uv_once(&jl_precompile_jobserver_once, jl_precompile_jobserver_init_lock); uv_mutex_lock(&jl_precompile_jobserver_lock); if (JL_PRECOMPILE_JOBSERVER_ACTIVE()) { #ifdef _OS_WINDOWS_ ReleaseSemaphore(jl_precompile_jobserver_sem, 1, NULL); #else sem_post(jl_precompile_jobserver_sem); #endif } uv_mutex_unlock(&jl_precompile_jobserver_lock); } // -- high resolution timers -- // Returns time in nanosec JL_DLLEXPORT uint64_t jl_hrtime(void) JL_NOTSAFEPOINT { return uv_hrtime(); } // -- access to std filehandles -- JL_STREAM *JL_STDIN = (JL_STREAM*)STDIN_FILENO; JL_STREAM *JL_STDOUT = (JL_STREAM*)STDOUT_FILENO; JL_STREAM *JL_STDERR = (JL_STREAM*)STDERR_FILENO; JL_DLLEXPORT JL_STREAM *jl_stdin_stream(void) { return JL_STDIN; } JL_DLLEXPORT JL_STREAM *jl_stdout_stream(void) { return JL_STDOUT; } JL_DLLEXPORT JL_STREAM *jl_stderr_stream(void) { return JL_STDERR; } JL_DLLEXPORT int jl_termios_size(void) { #if defined(_OS_WINDOWS_) return 0; #else return sizeof(struct termios); #endif } // -- processor native alignment information -- JL_DLLEXPORT void jl_native_alignment(uint_t *int8align, uint_t *int16align, uint_t *int32align, uint_t *int64align, uint_t *float32align, uint_t *float64align) { *int8align = __alignof(uint8_t); *int16align = __alignof(uint16_t); *int32align = __alignof(uint32_t); *int64align = __alignof(uint64_t); *float32align = __alignof(float); *float64align = __alignof(double); } JL_DLLEXPORT jl_value_t *jl_is_char_signed(void) { return ((char)255) < 0 ? jl_true : jl_false; } // -- misc sysconf info -- #ifdef _OS_WINDOWS_ static long cachedPagesize = 0; JL_DLLEXPORT long jl_getpagesize(void) { if (!cachedPagesize) { SYSTEM_INFO systemInfo; GetSystemInfo (&systemInfo); cachedPagesize = systemInfo.dwPageSize; } return cachedPagesize; } #else JL_DLLEXPORT long jl_getpagesize(void) { long page_size = sysconf(_SC_PAGESIZE); assert(page_size != -1); return page_size; } #endif #ifdef _OS_WINDOWS_ static long cachedAllocationGranularity = 0; JL_DLLEXPORT long jl_getallocationgranularity(void) JL_NOTSAFEPOINT { if (!cachedAllocationGranularity) { SYSTEM_INFO systemInfo; GetSystemInfo (&systemInfo); cachedAllocationGranularity = systemInfo.dwAllocationGranularity; } return cachedAllocationGranularity; } #else JL_DLLEXPORT long jl_getallocationgranularity(void) JL_NOTSAFEPOINT { return jl_getpagesize(); } #endif JL_DLLEXPORT long jl_gethugepagesize(void) JL_NOTSAFEPOINT { #if defined(_OS_LINUX_) long detected = 0; FILE *f = fopen("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size", "r"); if (f) { unsigned long long size = 0; if (fscanf(f, "%llu", &size) == 1 && size > 0) { detected = (long)size; } fclose(f); } if (detected == 0) { f = fopen("/proc/meminfo", "r"); if (f) { char line[256]; while (fgets(line, sizeof(line), f)) { unsigned long long kb = 0; if (sscanf(line, "Hugepagesize:%llu kB", &kb) == 1 && kb > 0) { detected = (long)(kb * 1024ULL); break; } } fclose(f); } } if (detected == 0) { detected = 2 * 1024 * 1024; // 2 MiB fallback } return detected; #else return 0; #endif } JL_DLLEXPORT long jl_SC_CLK_TCK(void) { #ifndef _OS_WINDOWS_ return sysconf(_SC_CLK_TCK); #else return 1000; /* uv_cpu_info returns times in ms on Windows */ #endif } #ifdef _OS_OPENBSD_ // Helper for jl_pathname_for_handle() struct dlinfo_data { void *searched; const char *result; }; static int dlinfo_helper(struct dl_phdr_info *info, size_t size, void *vdata) { struct dlinfo_data *data = (struct dlinfo_data *)vdata; void *handle; /* ensure dl_phdr_info at compile-time to be compatible with the one at runtime */ if (sizeof(*info) < size) return -1; /* dlopen the name */ handle = dlopen(info->dlpi_name, RTLD_LAZY | RTLD_NOLOAD); if (handle == NULL) return 0; /* check if the opened library is the same as the searched handle */ if (data->searched == handle) data->result = info->dlpi_name; dlclose(handle); /* continue if still not found */ return (data->result != NULL); } #endif // Takes a handle (as returned from dlopen()) and returns the absolute path to the image loaded #ifdef __APPLE__ // `JL_RTLD_NOLOAD` only asks whether an image is already loaded: nothing is mapped // and no initializer runs, so unlike a general `jl_dlopen` this cannot call back // into Julia, and may be used from a JL_NOTSAFEPOINT function. static void *jl_dlopen_noload(const char *filename) JL_NOTSAFEPOINT { #ifdef __clang_gcanalyzer__ // hidden from the checker, which only knows the general contract of jl_dlopen return NULL; #else return jl_dlopen(filename, JL_RTLD_DEFAULT | JL_RTLD_NOLOAD); #endif } #endif JL_DLLEXPORT const char *jl_pathname_for_handle(void *handle) JL_NOTSAFEPOINT { if (!handle) return NULL; #ifdef __APPLE__ // Iterate through all images currently in memory for (int32_t i = _dyld_image_count() - 1; i >= 0 ; i--) { // dlopen() each image, check handle. const char *image_name = _dyld_get_image_name(i); void *probe_lib = jl_dlopen_noload(image_name); if (!probe_lib) continue; jl_dlclose(probe_lib); // If the handle is the same as what was passed in (modulo mode bits), return this image name if (((intptr_t)handle & (-4)) == ((intptr_t)probe_lib & (-4))) return image_name; } #elif defined(_OS_WINDOWS_) wchar_t *pth16 = (wchar_t*)malloc_s(32768 * sizeof(*pth16)); // max long path length DWORD n16 = GetModuleFileNameW((HMODULE)handle, pth16, 32768); if (n16 <= 0) { free(pth16); return NULL; } char *filepath = NULL; size_t n8 = 0; if (uv_utf16_to_wtf8((uint16_t*)pth16, n16, &filepath, &n8)) { free(pth16); return NULL; } free(pth16); return filepath; #elif defined(_OS_OPENBSD_) struct dlinfo_data data = { .searched = handle, .result = NULL, }; dl_iterate_phdr(&dlinfo_helper, &data); return data.result; #else // Linux, FreeBSD, ... struct link_map *map; dlinfo(handle, RTLD_DI_LINKMAP, &map); #ifdef _COMPILER_MSAN_ENABLED_ __msan_unpoison(&map,sizeof(struct link_map*)); if (map) { __msan_unpoison(map, sizeof(struct link_map)); __msan_unpoison_string(map->l_name); } #endif if (map) return map->l_name; #endif return NULL; } #if !defined(_OS_WINDOWS_) && !defined(__APPLE__) && !defined(__GLIBC__) struct sym_phdr_query { uintptr_t addr; const char *name; // matched dlpi_name int is_main_exe; int found; int index; // running object index; 0 == main program }; static int sym_phdr_helper(struct dl_phdr_info *info, size_t size, void *vdata) JL_NOTSAFEPOINT { (void)size; struct sym_phdr_query *q = (struct sym_phdr_query *)vdata; int idx = q->index++; // Find the object whose mapped PT_LOAD segments contain `addr`. for (int i = 0; i < info->dlpi_phnum; i++) { if (info->dlpi_phdr[i].p_type != PT_LOAD) continue; uintptr_t beg = (uintptr_t)info->dlpi_addr + info->dlpi_phdr[i].p_vaddr; uintptr_t end = beg + info->dlpi_phdr[i].p_memsz; if (q->addr >= beg && q->addr < end) { q->name = info->dlpi_name; // Note that `dlpi_name` for the main executable is platform-dependent // and frequently `argv[0]` on non-GLIBC platforms. We do not want to // return that result from this API since it cannot be reliably dlopen'd. // // Return "" instead, which also cannot be dlopen'd either (on non-GLIBC // platforms) but which is at least a consistent value to match on for a // dlopen(NULL) fallback, which does give a handle to the main exe. q->is_main_exe = (idx == 0); // dl_iterate_phdr reports the main program first q->found = 1; return 1; // stop: found the containing object } } return 0; // keep searching } #endif // Takes the address of a symbol and returns the path to the image that contains // it, or NULL. On ELF, the main executable is reported as the empty string "". JL_DLLEXPORT const char *jl_pathname_for_symbol(void *symbol) JL_NOTSAFEPOINT { if (!symbol) return NULL; #ifdef _OS_WINDOWS_ HMODULE handle; if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCWSTR)symbol, &handle)) return NULL; return jl_pathname_for_handle(handle); #elif defined(__APPLE__) // dyld reports the real image path (including for the main executable). Dl_info info; if (!dladdr(symbol, &info) || !info.dli_fname) return NULL; return info.dli_fname; #elif defined(__GLIBC__) // glibc: dladdr1 hands us the containing object's link_map directly. Dl_info info; struct link_map *map = NULL; if (!dladdr1(symbol, &info, (void **)&map, RTLD_DL_LINKMAP) || map == NULL) return NULL; msan_unpoison(&map, sizeof(struct link_map *)); msan_unpoison(map, sizeof(struct link_map)); msan_unpoison_string(map->l_name); return map->l_name; // the empty string for the main executable #else // Other libc: walk the program headers and test PT_LOAD containment. struct sym_phdr_query q = { (uintptr_t)symbol, NULL, 0, 0, 0 }; dl_iterate_phdr(&sym_phdr_helper, &q); if (!q.found) return NULL; return q.is_main_exe ? "" : q.name; #endif } #ifdef _OS_WINDOWS_ // Get a list of all the modules in this process. JL_DLLEXPORT int jl_dllist(jl_array_t *list) { DWORD cb, cbNeeded; HMODULE *hMods = NULL; unsigned int i; cbNeeded = 1024 * sizeof(*hMods); do { cb = cbNeeded; hMods = (HMODULE*)realloc_s(hMods, cb); if (!EnumProcessModulesEx(GetCurrentProcess(), hMods, cb, &cbNeeded, LIST_MODULES_ALL)) { free(hMods); return FALSE; } } while (cb < cbNeeded); for (i = 0; i < cbNeeded / sizeof(HMODULE); i++) { const char *path = jl_pathname_for_handle(hMods[i]); if (path == NULL) continue; jl_array_grow_end((jl_array_t*)list, 1); jl_value_t *v = jl_cstr_to_string(path); free((char*)path); jl_array_ptr_set(list, jl_array_dim0(list) - 1, v); } free(hMods); return TRUE; } #elif !defined(__APPLE__) struct dllist_data { arraylist_t *names; // strdup'd object names (freed by jl_dllist) int index; }; // This runs under the dynamic linker lock (held by `dl_iterate_phdr` across the // callback), so it must not allocate Julia objects or otherwise hit a GC safepoint. // A stop-the-world here while another thread is blocked in the linker would deadlock static int dllist_helper(struct dl_phdr_info *info, size_t size, void *vdata) JL_NOTSAFEPOINT { (void)size; struct dllist_data *data = (struct dllist_data *)vdata; int idx = data->index++; // dl_iterate_phdr reports the main program first const char *name = info->dlpi_name; if (idx == 0 || name == NULL || name[0] == '\0') return 0; // skip the main executable and any unnamed objects arraylist_push(data->names, strdup(name)); return 0; } // Get a list of all the modules in this process. // // This is done in C, rather than a Julia `dl_iterate_phdr` callback, so that // no Julia code (which can allocate, run finalizers, or re-enter the linker // via (lazy) `ccall`) runs under the dynamic loader lock. See `dllist_helper` JL_DLLEXPORT int jl_dllist(jl_array_t *list) JL_CANSAFEPOINT { arraylist_t names; arraylist_new(&names, 0); struct dllist_data data = { &names, 0 }; dl_iterate_phdr(&dllist_helper, &data); // The loader lock is now released: safe to allocate Julia objects. for (size_t i = 0; i < names.len; i++) { char *name = (char*)names.items[i]; jl_array_grow_end(list, 1); jl_value_t *v = jl_cstr_to_string(name); jl_array_ptr_set(list, jl_array_dim0(list) - 1, v); free(name); } arraylist_free(&names); return 1; } #endif JL_DLLEXPORT void jl_raise_debugger(void) { #if defined(_OS_WINDOWS_) if (IsDebuggerPresent() == 1) DebugBreak(); #else raise(SIGTRAP); #endif // _OS_WINDOWS_ } JL_DLLEXPORT jl_sym_t *jl_get_UNAME(void) JL_NOTSAFEPOINT { return jl_symbol(JL_BUILD_UNAME); } JL_DLLEXPORT jl_sym_t *jl_get_ARCH(void) JL_NOTSAFEPOINT { return jl_symbol(JL_BUILD_ARCH); } JL_DLLEXPORT size_t jl_maxrss(void) { uv_rusage_t rusage; if (uv_getrusage(&rusage) == 0) { return rusage.ru_maxrss * 1024; } return 0; } // Simple `rand()` like function, with global seed and added thread-safety // (but slow and insecure) static _Atomic(uint64_t) g_rngseed; JL_DLLEXPORT uint64_t jl_rand(void) JL_NOTSAFEPOINT { uint64_t max = UINT64_MAX; uint64_t rngseed0 = jl_atomic_load_relaxed(&g_rngseed); uint64_t rngseed; uint64_t rnd; do { rngseed = rngseed0; rnd = cong(max, &rngseed); } while (!jl_atomic_cmpswap_relaxed(&g_rngseed, &rngseed0, rngseed)); return rnd; } JL_DLLEXPORT void jl_srand(uint64_t rngseed) JL_NOTSAFEPOINT { jl_atomic_store_relaxed(&g_rngseed, rngseed); } void jl_init_rand(void) { uint64_t rngseed; if (uv_random(NULL, NULL, &rngseed, sizeof(rngseed), 0, NULL)) { ios_puts("WARNING: Entropy pool not available to seed RNG; using ad-hoc entropy sources.\n", ios_stderr); rngseed = uv_hrtime(); rngseed ^= int64hash(uv_os_getpid()); } jl_srand(rngseed); srand(rngseed); } #ifdef __cplusplus } #endif