/* sys.c I/O and operating system utility functions */ #include "julia.h" #include "uv.h" #include #include #include #include #ifndef _OS_WINDOWS_ #include #include #include #include #include #endif #include #include #if defined(_OS_WINDOWS_) && !defined(_COMPILER_MINGW_) char *basename(char *); char *dirname(char *); #else #include #endif #include #ifdef __APPLE__ #include #include #endif #ifdef _OS_LINUX_ #include #endif #define __STDC_CONSTANT_MACROS #define __STDC_LIMIT_MACROS #include #ifdef __SSE__ #include #endif #if defined _MSC_VER #include #endif DLLEXPORT uint32_t jl_getutf8(ios_t *s) { uint32_t wc=0; ios_getutf8(s, &wc); return wc; } DLLEXPORT size_t jl_ios_size(ios_t *s) { return s->size; } DLLEXPORT int jl_sizeof_off_t(void) { return sizeof(off_t); } #ifndef _OS_WINDOWS_ DLLEXPORT off_t jl_lseek(int fd, off_t offset, int whence) { return lseek(fd, offset, whence); } DLLEXPORT ssize_t jl_pwrite(int fd, const void *buf, size_t count, off_t offset) { return pwrite(fd, buf, count, offset); } DLLEXPORT void *jl_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { return mmap(addr, length, prot, flags, fd, offset); } #else DLLEXPORT off_t jl_lseek(int fd, off_t offset, int whence) { return _lseek(fd, offset, whence); } #endif DLLEXPORT int jl_sizeof_ios_t(void) { return sizeof(ios_t); } DLLEXPORT long jl_ios_fd(ios_t *s) { return s->fd; } DLLEXPORT int32_t jl_nb_available(ios_t *s) { return (int32_t)(s->size - s->bpos); } DLLEXPORT int jl_ios_eof(ios_t *s) { if (ios_eof(s)) return 1; if (ios_readprep(s, 1) < 1) return 1; return 0; } // --- dir/file stuff --- DLLEXPORT int jl_sizeof_uv_fs_t(void) { return sizeof(uv_fs_t); } DLLEXPORT void jl_uv_fs_req_cleanup(uv_fs_t* req) { uv_fs_req_cleanup(req); } DLLEXPORT int jl_readdir(const char* path, uv_fs_t* readdir_req) { // Note that the flags field is mostly ignored by libuv return uv_fs_readdir(uv_default_loop(), readdir_req, path, 0 /*flags*/, NULL); } DLLEXPORT char* jl_uv_fs_t_ptr(uv_fs_t* req) { return (char*)req->ptr; } DLLEXPORT char* jl_uv_fs_t_ptr_offset(uv_fs_t* req, int offset) { return (char*)req->ptr + offset; } DLLEXPORT int jl_uv_fs_result(uv_fs_t *f) { return f->result; } // --- stat --- DLLEXPORT int jl_sizeof_stat(void) { return sizeof(uv_stat_t); } DLLEXPORT int32_t jl_stat(const char* path, char* statbuf) { 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(uv_default_loop(), &req, path, NULL); if (ret == 0) memcpy(statbuf, req.ptr, sizeof(uv_stat_t)); uv_fs_req_cleanup(&req); return ret; } DLLEXPORT int32_t jl_lstat(const char* path, char* statbuf) { uv_fs_t req; int ret; ret = uv_fs_lstat(uv_default_loop(), &req, path, NULL); if (ret == 0) memcpy(statbuf, req.ptr, sizeof(uv_stat_t)); uv_fs_req_cleanup(&req); return ret; } DLLEXPORT int32_t jl_fstat(int fd, char *statbuf) { uv_fs_t req; int ret; ret = uv_fs_fstat(uv_default_loop(), &req, fd, NULL); if (ret == 0) memcpy(statbuf, req.ptr, sizeof(uv_stat_t)); uv_fs_req_cleanup(&req); return ret; } DLLEXPORT unsigned int jl_stat_dev(char *statbuf) { return ((uv_stat_t*)statbuf)->st_dev; } DLLEXPORT unsigned int jl_stat_ino(char *statbuf) { return ((uv_stat_t*)statbuf)->st_ino; } DLLEXPORT unsigned int jl_stat_mode(char *statbuf) { return ((uv_stat_t*)statbuf)->st_mode; } DLLEXPORT unsigned int jl_stat_nlink(char *statbuf) { return ((uv_stat_t*)statbuf)->st_nlink; } DLLEXPORT unsigned int jl_stat_uid(char *statbuf) { return ((uv_stat_t*)statbuf)->st_uid; } DLLEXPORT unsigned int jl_stat_gid(char *statbuf) { return ((uv_stat_t*)statbuf)->st_gid; } DLLEXPORT unsigned int jl_stat_rdev(char *statbuf) { return ((uv_stat_t*)statbuf)->st_rdev; } DLLEXPORT uint64_t jl_stat_size(char *statbuf) { return ((uv_stat_t*)statbuf)->st_size; } DLLEXPORT uint64_t jl_stat_blksize(char *statbuf) { return ((uv_stat_t*)statbuf)->st_blksize; } DLLEXPORT uint64_t jl_stat_blocks(char *statbuf) { return ((uv_stat_t*)statbuf)->st_blocks; } /* // atime is stupid, let's not support it 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; } */ 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; } 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; } // --- buffer manipulation --- jl_array_t *jl_takebuf_array(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_takebuf(s, &n); a = jl_ptr_to_array_1d(jl_array_uint8_type, b, n-1, 1); } return a; } jl_value_t *jl_takebuf_string(ios_t *s) { jl_array_t *a = jl_takebuf_array(s); JL_GC_PUSH1(&a); jl_value_t *str = jl_array_to_string(a); JL_GC_POP(); return str; } // the returned buffer must be manually freed. To determine the size, // call position(s) before using this function. void *jl_takebuf_raw(ios_t *s) { size_t sz; void *buf = ios_takebuf(s, &sz); return buf; } jl_value_t *jl_readuntil(ios_t *s, uint8_t delim) { jl_array_t *a; // manually inlined common case char *pd = (char*)memchr(s->buf+s->bpos, delim, s->size - s->bpos); if (pd) { size_t n = pd-(s->buf+s->bpos)+1; a = jl_alloc_array_1d(jl_array_uint8_type, n); memcpy(jl_array_data(a), s->buf+s->bpos, n); s->bpos += n; } else { a = jl_alloc_array_1d(jl_array_uint8_type, 80); ios_t dest; ios_mem(&dest, 0); ios_setbuf(&dest, (char*)a->data, 80, 0); size_t n = ios_copyuntil(&dest, s, delim); if (dest.buf != a->data) { a = jl_takebuf_array(&dest); } else { #ifdef STORE_ARRAY_LEN a->length = n; #endif a->nrows = n; ((char*)a->data)[n] = '\0'; } } return (jl_value_t*)a; } void jl_free2(void *p, void *hint) { free(p); } // -- syscall utilities -- int jl_errno(void) { return errno; } // -- get the number of CPU cores -- #ifdef _OS_WINDOWS_ typedef DWORD (WINAPI *GAPC)(WORD); #ifndef ALL_PROCESSOR_GROUPS #define ALL_PROCESSOR_GROUPS 0xffff #endif #endif DLLEXPORT int jl_cpu_cores(void) { #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; } } return count; #elif defined(_SC_NPROCESSORS_ONLN) return sysconf(_SC_NPROCESSORS_ONLN); #elif defined(_OS_WINDOWS_) //Try to get WIN7 API method GAPC gapc = (GAPC) jl_dlsym_e( jl_kernel32_handle, "GetActiveProcessorCount" ); if (gapc) { return gapc(ALL_PROCESSOR_GROUPS); } else { //fall back on GetSystemInfo SYSTEM_INFO info; GetSystemInfo(&info); return info.dwNumberOfProcessors; } #else return 1; #endif } // -- high resolution timers -- // Returns time in nanosec DLLEXPORT uint64_t jl_hrtime(void) { return uv_hrtime(); } // -- iterating the environment -- #ifdef __APPLE__ #include #else #if !defined(_OS_WINDOWS_) || defined(_COMPILER_MINGW_) extern char **environ; #endif #endif jl_value_t *jl_environ(int i) { #ifdef __APPLE__ char **environ = *_NSGetEnviron(); #endif char *env = environ[i]; return env ? jl_pchar_to_string(env, strlen(env)) : jl_nothing; } #ifdef _OS_WINDOWS_ jl_value_t *jl_env_done(char *pos) { return (*pos==0)?jl_true:jl_false; } #endif // -- child process status -- #if defined _MSC_VER || defined _OS_WINDOWS_ /* Native Woe32 API. */ #include #define waitpid(pid,statusp,options) _cwait (statusp, pid, WAIT_CHILD) #define WAIT_T int #define WTERMSIG(x) ((x) & 0xff) /* or: SIGABRT ?? */ #define WCOREDUMP(x) 0 #define WEXITSTATUS(x) (((x) >> 8) & 0xff) /* or: (x) ?? */ #define WIFSIGNALED(x) (WTERMSIG (x) != 0) /* or: ((x) == 3) ?? */ #define WIFEXITED(x) (WTERMSIG (x) == 0) /* or: ((x) != 3) ?? */ #define WIFSTOPPED(x) 0 #define WSTOPSIG(x) 0 //Is this correct? #endif int jl_process_exited(int status) { return WIFEXITED(status); } int jl_process_signaled(int status) { return WIFSIGNALED(status); } int jl_process_stopped(int status) { return WIFSTOPPED(status); } int jl_process_exit_status(int status) { return WEXITSTATUS(status); } int jl_process_term_signal(int status) { return WTERMSIG(status); } int jl_process_stop_signal(int status) { return WSTOPSIG(status); } // -- access to std filehandles -- JL_STREAM *JL_STDIN=0; JL_STREAM *JL_STDOUT=0; JL_STREAM *JL_STDERR=0; JL_STREAM *jl_stdin_stream(void) { return (JL_STREAM*)JL_STDIN; } JL_STREAM *jl_stdout_stream(void) { return (JL_STREAM*)JL_STDOUT; } JL_STREAM *jl_stderr_stream(void) { return (JL_STREAM*)JL_STDERR; } // -- set/clear the FZ/DAZ flags on x86 & x86-64 -- #ifdef __SSE__ #ifdef _OS_WINDOWS_ #define cpuid __cpuid #else void cpuid(int32_t CPUInfo[4], int32_t InfoType) { __asm__ __volatile__ ( #if defined(__i386__) && defined(__PIC__) "xchg %%ebx, %%esi;" "cpuid;" "xchg %%esi, %%ebx;": "=S" (CPUInfo[1]) , #else "cpuid": "=b" (CPUInfo[1]), #endif "=a" (CPUInfo[0]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) : "a" (InfoType) ); } #endif DLLEXPORT uint8_t jl_zero_subnormals(uint8_t isZero) { uint32_t flags = 0x00000000; int32_t info[4]; cpuid(info, 0); if (info[0] >= 1) { cpuid(info, 0x00000001); if ((info[3] & ((int)1 << 26)) != 0) { // SSE2 supports both FZ and DAZ flags = 0x00008040; } else if ((info[3] & ((int)1 << 25)) != 0) { // SSE supports only the FZ flag flags = 0x00008000; } } if (flags) { if (isZero) { _mm_setcsr(_mm_getcsr() | flags); } else { _mm_setcsr(_mm_getcsr() & ~flags); } return 1; } return 0; } #else DLLEXPORT uint8_t jl_zero_subnormals(uint8_t isZero) { return 0; } #endif // -- processor native alignment information -- DLLEXPORT void jl_native_alignment(uint_t* int8align, uint_t* int16align, uint_t* int32align, uint_t* int64align, uint_t* float32align, uint_t* float64align) { LLVMTargetDataRef tgtdata = LLVMCreateTargetData(""); *int8align = LLVMPreferredAlignmentOfType(tgtdata, LLVMInt8Type()); *int16align = LLVMPreferredAlignmentOfType(tgtdata, LLVMInt16Type()); *int32align = LLVMPreferredAlignmentOfType(tgtdata, LLVMInt32Type()); *int64align = LLVMPreferredAlignmentOfType(tgtdata, LLVMInt64Type()); *float32align = LLVMPreferredAlignmentOfType(tgtdata, LLVMFloatType()); *float64align = LLVMPreferredAlignmentOfType(tgtdata, LLVMDoubleType()); LLVMDisposeTargetData(tgtdata); } DLLEXPORT jl_value_t *jl_is_char_signed() { return ((char)255) < 0 ? jl_true : jl_false; } DLLEXPORT void jl_field_offsets(jl_datatype_t *dt, ssize_t *offsets) { size_t i; for(i=0; i < jl_tuple_len(dt->names); i++) { offsets[i] = jl_field_offset(dt, i); } } // -- misc sysconf info -- #ifdef _OS_WINDOWS_ static long chachedPagesize = 0; long jl_getpagesize(void) { if (!chachedPagesize) { SYSTEM_INFO systemInfo; GetSystemInfo (&systemInfo); chachedPagesize = systemInfo.dwPageSize; } return chachedPagesize; } #else long jl_getpagesize(void) { return sysconf(_SC_PAGESIZE); } #endif DLLEXPORT long jl_SC_CLK_TCK(void) { #ifndef _OS_WINDOWS_ return sysconf(_SC_CLK_TCK); #else return 0; #endif } // Dynamic Library interrogation #ifdef __APPLE__ // This code gratefully lifted from: http://stackoverflow.com/questions/20481058 #ifdef __LP64__ typedef struct mach_header_64 mach_header_t; typedef struct segment_command_64 segment_command_t; typedef struct nlist_64 nlist_t; #else typedef struct mach_header mach_header_t; typedef struct segment_command segment_command_t; typedef struct nlist nlist_t; #endif static const char *first_external_symbol_for_image(const mach_header_t *header) { Dl_info info; if (dladdr(header, &info) == 0) return NULL; segment_command_t *seg_linkedit = NULL; segment_command_t *seg_text = NULL; struct symtab_command *symtab = NULL; struct load_command *cmd = (struct load_command *)((intptr_t)header + sizeof(mach_header_t)); for (uint32_t i = 0; i < header->ncmds; i++, cmd = (struct load_command *)((intptr_t)cmd + cmd->cmdsize)) { switch(cmd->cmd) { case LC_SEGMENT: case LC_SEGMENT_64: if (!strcmp(((segment_command_t *)cmd)->segname, SEG_TEXT)) seg_text = (segment_command_t *)cmd; else if (!strcmp(((segment_command_t *)cmd)->segname, SEG_LINKEDIT)) seg_linkedit = (segment_command_t *)cmd; break; case LC_SYMTAB: symtab = (struct symtab_command *)cmd; break; } } if ((seg_text == NULL) || (seg_linkedit == NULL) || (symtab == NULL)) return NULL; intptr_t file_slide = ((intptr_t)seg_linkedit->vmaddr - (intptr_t)seg_text->vmaddr) - seg_linkedit->fileoff; intptr_t strings = (intptr_t)header + (symtab->stroff + file_slide); nlist_t *sym = (nlist_t *)((intptr_t)header + (symtab->symoff + file_slide)); for (uint32_t i = 0; i < symtab->nsyms; i++, sym++) { if ((sym->n_type & N_EXT) != N_EXT || !sym->n_value) continue; return (const char*)strings + sym->n_un.n_strx; } return NULL; } #endif // Takes a handle (as returned from dlopen()) and returns the absolute path to the image loaded DLLEXPORT const char *jl_pathname_for_handle(uv_lib_t *uv_lib) { if (!uv_lib) return NULL; void *handle = uv_lib->handle; #ifdef __APPLE__ for (int32_t i = _dyld_image_count(); i >= 0 ; i--) { const char *first_symbol = first_external_symbol_for_image((const mach_header_t *)_dyld_get_image_header(i)); if (first_symbol && strlen(first_symbol) > 1) { handle = (void*)((intptr_t)handle | 1); // in order to trigger findExportedSymbol instead of findExportedSymbolInImageOrDependentImages. See `dlsym` implementation at http://opensource.apple.com/source/dyld/dyld-239.3/src/dyldAPIs.cpp first_symbol++; // in order to remove the leading underscore void *address = dlsym(handle, first_symbol); Dl_info info; if (dladdr(address, &info)) return info.dli_fname; } } #endif #ifdef _OS_LINUX_ struct link_map *map; dlinfo(handle, RTLD_DI_LINKMAP, &map); if (map) return map->l_name; #endif #ifdef _OS_WINDOWS_ // Not supported yet... #endif return NULL; }