/* * Copyright (c) Advanced Micro Devices, Inc., or its affiliates. * * SPDX-License-Identifier: MIT */ #if !defined(_WIN32) && !defined(__CYGWIN__) #include #include #include "os/os.hpp" #include "thread/thread.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef DT_GNU_HASH #define DT_GNU_HASH 0x6ffffef5 #endif // DT_GNU_HASH #include #include #include #include #include // for strncmp #include #include // for tempnam #include #include #include #include #include namespace amd { namespace { struct ThreadAffinityState { std::vector runtime_set_affinity_mask; bool runtime_set_affinity_mask_valid = false; uint32_t runtime_set_affinity_node = static_cast(-1); std::vector original_affinity_mask; bool original_affinity_mask_valid = false; }; ThreadAffinityState*& GetThreadAffinityStatePtr() { thread_local ThreadAffinityState* state = nullptr; return state; } ThreadAffinityState& GetThreadAffinityState() { // Runtime teardown can run after non-trivial TLS destructors at process exit. // Keep only a trivial TLS pointer so affinity reset never observes destroyed vector storage. ThreadAffinityState*& state = GetThreadAffinityStatePtr(); if (state == nullptr) { state = new ThreadAffinityState(); } return *state; } void ReleaseThreadAffinityState() { ThreadAffinityState*& state = GetThreadAffinityStatePtr(); delete state; state = nullptr; } bool GetCurrentThreadAffinity(std::vector* affinity) { const uint32_t size = CPU_ALLOC_SIZE(amd::Os::processorCount()); affinity->assign((size + sizeof(uint64_t) - 1) / sizeof(uint64_t), 0); if (syscall(__NR_sched_getaffinity, 0, size, affinity->data()) < 0) { ClPrint(amd::LOG_DEBUG, amd::LOG_RESOURCE, "syscall(__NR_sched_getaffinity, size=%u) failed", size); return false; } return true; } bool SetCurrentThreadAffinity(const std::vector& affinity) { const uint32_t size = static_cast(affinity.size() * sizeof(uint64_t)); if (syscall(__NR_sched_setaffinity, 0, size, affinity.data()) < 0) { ClPrint(amd::LOG_DEBUG, amd::LOG_RESOURCE, "syscall(__NR_sched_setaffinity, size=%u) failed", size); return false; } return true; } bool IsCpuSet(const std::vector& affinity, uint32_t cpu) { const uint32_t index = cpu / numa::kBitsPerUInt64; return index < affinity.size() && ((affinity[index] >> (cpu % numa::kBitsPerUInt64)) & 1); } bool IsUnrestrictedAffinity(const std::vector& affinity) { for (uint32_t cpu = 0; cpu < static_cast(amd::Os::processorCount()); ++cpu) { if (!IsCpuSet(affinity, cpu)) { return false; } } return true; } bool IsSameAffinity(const std::vector& lhs, const std::vector& rhs) { const size_t size = std::max(lhs.size(), rhs.size()); for (size_t i = 0; i < size; ++i) { const uint64_t l = (i < lhs.size()) ? lhs[i] : 0; const uint64_t r = (i < rhs.size()) ? rhs[i] : 0; if (l != r) { return false; } } return true; } } // namespace // Crash signal handling static const int kCrashSignals[] = {SIGSEGV, SIGABRT, SIGBUS, SIGILL, SIGFPE}; static constexpr size_t kNumCrashSignals = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); static struct sigaction oldCrashHandlers_[kNumCrashSignals]; static Os::CrashCallback crashCallback_ = nullptr; static std::atomic crashHandlersInstalled_{false}; static void crashSignalHandler(int sig) { if (crashCallback_ != nullptr) { crashCallback_(); } kill(getpid(), sig); } bool Os::installExceptionHandlers(CrashCallback callback) { if (crashHandlersInstalled_.exchange(true)) { return true; } crashCallback_ = callback; struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_handler = crashSignalHandler; sa.sa_flags = SA_RESETHAND; for (size_t i = 0; i < kNumCrashSignals; ++i) { if (sigaction(kCrashSignals[i], &sa, &oldCrashHandlers_[i]) != 0) { return false; } } return true; } void Os::uninstallExceptionHandlers() { if (!crashHandlersInstalled_.load()) { return; } for (size_t i = 0; i < kNumCrashSignals; ++i) { sigaction(kCrashSignals[i], &oldCrashHandlers_[i], nullptr); } crashCallback_ = nullptr; crashHandlersInstalled_.store(false); } static struct sigaction oldSigAction; static bool callOldSignalHandler(int sig, siginfo_t* info, void* ptr) { if (oldSigAction.sa_handler == SIG_DFL) { // no signal handler was previously installed. return false; } else if (oldSigAction.sa_handler != SIG_IGN) { if ((oldSigAction.sa_flags & SA_NODEFER) == 0) { sigaddset(&oldSigAction.sa_mask, sig); } void (*handler)(int) = oldSigAction.sa_handler; if (oldSigAction.sa_flags & SA_RESETHAND) { oldSigAction.sa_handler = SIG_DFL; } sigset_t savedSigSet; pthread_sigmask(SIG_SETMASK, &oldSigAction.sa_mask, &savedSigSet); if (oldSigAction.sa_flags & SA_SIGINFO) { oldSigAction.sa_sigaction(sig, info, ptr); } else { handler(sig); } pthread_sigmask(SIG_SETMASK, &savedSigSet, NULL); } return true; } static void divisionErrorHandler(int sig, siginfo_t* info, void* ptr) { assert(info != NULL && ptr != NULL && "just checking"); ucontext_t* uc = (ucontext_t*)ptr; address insn; #if defined(ATI_ARCH_X86) insn = (address)uc->uc_mcontext.gregs[LP64_SWITCH(REG_EIP, REG_RIP)]; #else assert(!"Unimplemented"); #endif if (Thread::current()->isWorkerThread()) { if (Os::skipIDIV(insn)) { #if defined(ATI_ARCH_X86) uc->uc_mcontext.gregs[LP64_SWITCH(REG_EIP, REG_RIP)] = (greg_t)insn; #else assert(!"Unimplemented"); #endif return; } } // Call the chained signal handler if (callOldSignalHandler(sig, info, ptr)) { return; } std::cerr << "Unhandled signal in divisionErrorHandler()" << std::endl; ::abort(); } typedef int (*pthread_setaffinity_fn)(pthread_t, size_t, const cpu_set_t*); static pthread_setaffinity_fn pthread_setaffinity_fptr; static void init() __attribute__((constructor(101))); static void init() { Os::init(); } static cpu_set_t nativeMask_; bool Os::installSigfpeHandler() { // Install a SIGFPE signal handler @todo: Chain the handlers struct sigaction sa; sigfillset(&sa.sa_mask); sa.sa_handler = SIG_DFL; sa.sa_sigaction = divisionErrorHandler; sa.sa_flags = SA_SIGINFO | SA_RESTART; if (sigaction(SIGFPE, &sa, &oldSigAction) != 0) { return false; } return true; } void Os::uninstallSigfpeHandler() {} bool Os::init() { static bool initialized_ = false; // We could use pthread_once here: if (initialized_) { return true; } initialized_ = true; pageSize_ = (size_t)::sysconf(_SC_PAGESIZE); processorCount_ = ::sysconf(_SC_NPROCESSORS_CONF); pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &nativeMask_); pthread_setaffinity_fptr = (pthread_setaffinity_fn)dlsym(RTLD_NEXT, "pthread_setaffinity_np"); return Thread::init(); } static void __exit() __attribute__((destructor(101))); static void __exit() { Os::tearDown(); } void Os::tearDown() { Thread::tearDown(); } void* Os::loadLibrary_(const char* filename) { return (*filename == '\0') ? NULL : ::dlopen(filename, RTLD_LAZY); } void Os::unloadLibrary(void* handle) { ::dlclose(handle); } void* Os::getSymbol(void* handle, const char* name) { return ::dlsym(handle, name); } static inline int memProtToOsProt(Os::MemProt prot) { switch (prot) { case Os::MEM_PROT_NONE: return PROT_NONE; case Os::MEM_PROT_READ: return PROT_READ; case Os::MEM_PROT_RW: return PROT_READ | PROT_WRITE; case Os::MEM_PROT_RWX: return PROT_READ | PROT_WRITE | PROT_EXEC; default: break; } ShouldNotReachHere(); return -1; } address Os::reserveMemory(address start, size_t size, size_t alignment, MemProt prot) { size = alignUp(size, pageSize()); // check for invalid input size if (size == 0) { return NULL; } alignment = std::max(pageSize(), alignUp(alignment, pageSize())); assert(isPowerOfTwo(alignment) && "not a power of 2"); size_t requested = size + alignment - pageSize(); address mem = (address)::mmap(start, requested, memProtToOsProt(prot), MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS, 0, 0); // check for out of memory if (mem == MAP_FAILED) return NULL; address aligned = alignUp(mem, alignment); // return the unused leading pages to the free state if (&aligned[0] != &mem[0]) { assert(&aligned[0] > &mem[0] && "check this code"); if (::munmap(&mem[0], &aligned[0] - &mem[0]) != 0) { assert(!"::munmap failed"); } } // return the unused trailing pages to the free state if (&aligned[size] != &mem[requested]) { assert(&aligned[size] < &mem[requested] && "check this code"); if (::munmap(&aligned[size], &mem[requested] - &aligned[size]) != 0) { assert(!"::munmap failed"); } } // Hint to enable THP for large host allocations which can help in performance gain constexpr size_t kLargePageSize = 2 * Mi; if (size >= kLargePageSize) { int status = madvise(aligned, size, MADV_HUGEPAGE); if (status) { ClPrint(amd::LOG_DEBUG, amd::LOG_CODE, "madvise with advice MADV_HUGEPAGE" " starting at address %p and page size 0x%zx, returned %d, errno: %s", aligned, size, status, strerror(errno)); } } return aligned; } bool Os::releaseMemory(void* addr, size_t size) { assert(isMultipleOf(addr, pageSize()) && "not page aligned!"); size = alignUp(size, pageSize()); return 0 == ::munmap(addr, size); } bool Os::commitMemory(void* addr, size_t size, MemProt prot) { assert(isMultipleOf(addr, pageSize()) && "not page aligned!"); size = alignUp(size, pageSize()); return ::mmap(addr, size, memProtToOsProt(prot), MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0) != MAP_FAILED; } bool Os::uncommitMemory(void* addr, size_t size) { assert(isMultipleOf(addr, pageSize()) && "not page aligned!"); size = alignUp(size, pageSize()); return ::mmap(addr, size, PROT_NONE, MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANONYMOUS, -1, 0) != MAP_FAILED; } bool Os::protectMemory(void* addr, size_t size, MemProt prot) { assert(isMultipleOf(addr, pageSize()) && "not page aligned!"); size = alignUp(size, pageSize()); return 0 == ::mprotect(addr, size, memProtToOsProt(prot)); } uint64_t Os::hostTotalPhysicalMemory() { static uint64_t totalPhys = 0; if (totalPhys != 0) { return totalPhys; } totalPhys = sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES); return totalPhys; } void* Os::alignedMalloc(size_t size, size_t alignment) { void* ptr = NULL; if (0 == ::posix_memalign(&ptr, alignment, size)) { return ptr; } return NULL; } void Os::alignedFree(void* mem) { ::free(mem); } void Os::currentStackInfo(address* base, size_t* size) { // There could be some issue trying to get the pthread_attr of // the primordial thread if the pthread library is not present // at load time (a binary loads the OpenCL/HIP app/runtime dynamically. // We should look into this... -laurent pthread_t self = ::pthread_self(); pthread_attr_t threadAttr; if (0 != ::pthread_getattr_np(self, &threadAttr)) { fatal("pthread_getattr_np() failed"); } if (0 != ::pthread_attr_getstack(&threadAttr, (void**)base, size)) { fatal("pthread_attr_getstack() failed"); } *base += *size; ::pthread_attr_destroy(&threadAttr); assert(Os::currentStackPtr() >= *base - *size && Os::currentStackPtr() < *base && "just checking"); } void Os::setCurrentThreadName(const char* name) { ::prctl(PR_SET_NAME, name); } void* Thread::entry(Thread* thread) { sigset_t set; sigfillset(&set); pthread_sigmask(SIG_BLOCK, &set, NULL); sigemptyset(&set); sigaddset(&set, SIGFPE); pthread_sigmask(SIG_UNBLOCK, &set, NULL); return thread->main(); } bool Os::isThreadAlive(const Thread& thread) { return ::pthread_kill((pthread_t)thread.handle(), 0) == 0; } const void* Os::createOsThread(amd::Thread* thread) { pthread_attr_t threadAttr; ::pthread_attr_init(&threadAttr); if (thread->stackSize_ != 0) { size_t guardsize = 0; if (0 != ::pthread_attr_getguardsize(&threadAttr, &guardsize)) { fatal("pthread_attr_getguardsize() failed"); } if (0 != ::pthread_attr_setstacksize(&threadAttr, thread->stackSize_ + guardsize)) { fatal("pthread_attr_setstacksize() failed"); } } // We never plan the use join, so free the resources now. if (0 != ::pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED)) { fatal("pthread_attr_setdetachstate() failed"); } pthread_t handle = 0; if (0 != ::pthread_create(&handle, &threadAttr, (void* (*)(void*)) & Thread::entry, thread)) { thread->setState(Thread::FAILED); guarantee(false, "pthread_create() failed"); } if (0 != ::pthread_attr_destroy(&threadAttr)) { fatal("pthread_attr_destroy() failed"); }; return reinterpret_cast(handle); } void Os::yield() { ::sched_yield(); } uint64_t Os::timeNanos() { struct timespec tp; ::clock_gettime(CLOCK_MONOTONIC, &tp); return (uint64_t)tp.tv_sec * (1000ULL * 1000ULL * 1000ULL) + (uint64_t)tp.tv_nsec; } uint64_t Os::timerResolutionNanos() { static uint64_t resolution = 0; if (resolution == 0) { struct timespec tp; ::clock_getres(CLOCK_MONOTONIC, &tp); resolution = (uint64_t)tp.tv_sec * (1000ULL * 1000ULL * 1000ULL) + (uint64_t)tp.tv_nsec; } return resolution; } const char* Os::libraryExtension() { return MACOS_SWITCH(".dylib", ".so"); } const char* Os::libraryPrefix() { return "lib"; } const char* Os::objectExtension() { return ".o"; } char Os::fileSeparator() { return '/'; } char Os::pathSeparator() { return ':'; } bool Os::pathExists(const std::string& path) { struct stat st; if (stat(path.c_str(), &st) != 0) return false; return S_ISDIR(st.st_mode); } bool Os::createPath(const std::string& path) { mode_t mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH; size_t pos = 0; while (true) { pos = path.find(fileSeparator(), pos); const std::string currPath = path.substr(0, pos); if (!currPath.empty() && !pathExists(currPath)) { int ret = mkdir(currPath.c_str(), mode); if (ret == -1) return false; } if (pos == std::string::npos) break; ++pos; } return true; } bool Os::removePath(const std::string& path) { size_t pos = std::string::npos; bool removed = false; while (true) { const std::string currPath = path.substr(0, pos); if (!currPath.empty()) { int ret = rmdir(currPath.c_str()); if (ret == -1) return removed; removed = true; } if (pos == 0) break; pos = path.rfind(fileSeparator(), pos == std::string::npos ? pos : pos - 1); if (pos == std::string::npos) break; } return true; } int Os::printf(const char* fmt, ...) { va_list ap; va_start(ap, fmt); int len = ::vprintf(fmt, ap); va_end(ap); return len; } // Os::systemCall() // ================ // Execute a program and return the program exitcode or -1 if there were problems. // The input argument 'command' is expected to be a space separated string of // command-line arguments with arguments containing spaces between double-quotes. // // In order to avoid duplication of memory, we use vfork()+exec(). vfork() has // potiential security risks; // // In spite of these risks, the alternatives (system() or fork()) create resource // issues when running conformance test_allocation which stretches the system // memory to its limits. Thus we will accept this compromise under the condition // that the runtime will soon remove any need to call out to external commands. // // Note that stdin/stdout/stderr of the command are sent to /dev/null. // int Os::systemCall(const std::string& command) { #if 1 size_t len = command.size(); char* cmd = new char[len + 1]; std::memcpy(cmd, command.c_str(), len); cmd[len] = 0; // Split the command into arguments. This is a very // simple parser that only takes care of quotes and // doesn't support escaping with back-slash. In // the future, Os::systemCall() will either // disappear or it will be replaced with an // argc/argv interface. This parser also assumes // that if an argument is quoted, the whole // argument starts and ends with a double-quote. bool inQuote = false; int argLength = 0; int n = 0; char* cp = cmd; while (*cp) { switch (static_cast(*cp)) { case ' ': if (inQuote) { ++argLength; } else { *cp = '\0'; argLength = 0; } break; case '"': if (inQuote) { inQuote = false; *cp = '\0'; } else { inQuote = true; *cp = '\0'; argLength = 1; ++n; } break; default: if (++argLength == 1) { ++n; } break; } ++cp; } char** argv = new char*[n + 1]; int argc = 0; cp = cmd; do { while ('\0' == *cp) { ++cp; } argv[argc++] = cp; while ('\0' != *cp) { ++cp; } } while (argc < n); argv[argc] = NULL; int ret = -1; pid_t pid = vfork(); if (0 == pid) { // Child. Redirect stdin/stdout/stderr to /dev/null int fdIn = open("/dev/null", O_RDONLY); int fdOut = open("/dev/null", O_WRONLY); if (0 <= fdIn || 0 <= fdOut) { dup2(fdIn, 0); dup2(fdOut, 1); dup2(fdOut, 2); // Execute the program execvp(argv[0], argv); } _exit(-1); } else if (0 > pid) { // Can't vfork } else { // Parent - wait for program to complete and get exit code. int exitCode; if (0 <= waitpid(pid, &exitCode, 0)) { ret = exitCode; } } delete[] argv; delete[] cmd; return ret; #else return ::system(command.c_str()); #endif } std::string Os::getEnvironment(const std::string& name) { char* dstBuf; dstBuf = ::getenv(name.c_str()); if (dstBuf == NULL) { return std::string(""); } return std::string(dstBuf); } std::string Os::getTempPath() { std::string tempFolder = amd::Os::getEnvironment("TEMP"); if (tempFolder.empty()) { tempFolder = amd::Os::getEnvironment("TMP"); } if (tempFolder.empty()) { tempFolder = "/tmp"; ; } return tempFolder; } std::string Os::getTempFileName() { static std::atomic_size_t counter(0); std::string tempPath = getTempPath(); std::stringstream tempFileName; tempFileName << tempPath << "/OCL" << ::getpid() << 'T' << counter++; return tempFileName.str(); } int Os::unlink(const std::string& path) { return ::unlink(path.c_str()); } int Os::shm_unlink(const std::string& path) { return ::shm_unlink(path.c_str()); } #if defined(ATI_ARCH_X86) void Os::cpuid(int regs[4], int info) { #ifdef _LP64 __asm__ __volatile__( "movq %%rbx, %%rsi;" "cpuid;" "xchgq %%rbx, %%rsi;" : "=a"(regs[0]), "=S"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) : "a"(info)); #else __asm__ __volatile__( "movl %%ebx, %%esi;" "cpuid;" "xchgl %%ebx, %%esi;" : "=a"(regs[0]), "=S"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) : "a"(info)); #endif } uint64_t Os::xgetbv(uint32_t ecx) { uint32_t eax, edx; __asm__ __volatile__(".byte 0x0f,0x01,0xd0" // in case assembler doesn't recognize xgetbv : "=a"(eax), "=d"(edx) : "c"(ecx)); return ((uint64_t)edx << 32) | (uint64_t)eax; } bool Os::hasMovdir64b() { // CPUID leaf 7, sub-leaf 0: ECX bit 28 = MOVDIR64B. static const bool supported = [] { int regs[4]; #ifdef _LP64 __asm__ __volatile__( "movq %%rbx, %%rsi;" "cpuid;" "xchgq %%rbx, %%rsi;" : "=a"(regs[0]), "=S"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) : "a"(7), "c"(0)); #else __asm__ __volatile__( "movl %%ebx, %%esi;" "cpuid;" "xchgl %%ebx, %%esi;" : "=a"(regs[0]), "=S"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) : "a"(7), "c"(0)); #endif return static_cast((regs[2] >> 28) & 1); }(); return supported; } #endif // ATI_ARCH_X86 uint64_t Os::offsetToEpochNanos() { static uint64_t offset = 0; if (offset != 0) { return offset; } struct timeval now; if (::gettimeofday(&now, NULL) != 0) { return 0; } offset = (now.tv_sec * UINT64_C(1000000) + now.tv_usec) * UINT64_C(1000) - timeNanos(); return offset; } address Os::currentStackPtr() { intptr_t value; #if defined(__GNUC__) __asm__ __volatile__( #if defined(ATI_ARCH_X86) LP64_SWITCH("movl %%esp", "movq %%rsp") ",%0" : "=r"(value) #elif defined(ATI_ARCH_ARM) "mov %0,sp" : "=r"(value) #else "" #endif ); #else // !__GNUC__ __asm mov value, esp; #endif // !__GNUC__ return (address)value; } size_t Os::getPhysicalMemSize() { struct ::sysinfo si; if (::sysinfo(&si) != 0) { return 0; } if (si.mem_unit == 0) { // Linux kernels prior to 2.3.23 return sizes in bytes. si.mem_unit = 1; } return (size_t)si.totalram * si.mem_unit; } void Os::getAppPathAndFileName(std::string& appName, std::string& appPathAndName) { std::unique_ptr buff(new char[FILE_PATH_MAX_LENGTH]()); if (readlink("/proc/self/exe", buff.get(), FILE_PATH_MAX_LENGTH) > 0) { // Get filename without path and extension. appName = std::string(basename(buff.get())); appPathAndName = std::string(buff.get()); } else { appName = ""; appPathAndName = ""; } return; } bool Os::GetURIFromMemory(const void* image, size_t image_size, std::string& uri) { pid_t pid = getpid(); std::ostringstream uri_stream; // Create a unique resource indicator to the memory address uri_stream << "memory://" << pid << "#offset=0x" << std::hex << (uintptr_t)image << std::dec << "&size=" << image_size; uri = uri_stream.str(); return true; } bool Os::CloseFileHandle(FileDesc fdesc) { // Return false if close system call fails if (close(fdesc) < 0) { return false; } return true; } amd::Os::FileDesc Os::DupFileHandle(FileDesc fdesc) { return dup(fdesc); } bool Os::GetFileHandle(const char* fname, FileDesc* fd_ptr, size_t* sz_ptr) { if ((fd_ptr == nullptr) || (sz_ptr == nullptr)) { return false; } // open system function call, return false on fail struct stat stat_buf; *fd_ptr = open(fname, O_RDONLY); if (*fd_ptr < 0) { return false; } // Retrieve stat info and size if (fstat(*fd_ptr, &stat_buf) != 0) { close(*fd_ptr); return false; } *sz_ptr = stat_buf.st_size; return true; } bool amd::Os::FindFileNameFromAddress(const void* image, std::string* fname_ptr, size_t* foffset_ptr) { // Get the list of mapped file list bool ret_value = false; std::ifstream proc_maps; proc_maps.open("/proc/self/maps", std::ifstream::in); if (!proc_maps.is_open() || !proc_maps.good()) { return ret_value; } // For every line on the list map find out low, high address std::string line; while (std::getline(proc_maps, line)) { char dash; std::stringstream tokens(line); uintptr_t low_address, high_address; tokens >> std::hex >> low_address >> std::dec >> dash >> std::hex >> high_address >> std::dec; if (dash != '-') { continue; } // If address is > low_address and < high_address, then this // is the mapped file. Get the URI path and offset. uintptr_t address = reinterpret_cast(image); if ((address >= low_address) && (address < high_address)) { std::string permissions, device, uri_file_path; size_t offset; uint64_t inode; tokens >> permissions >> std::hex >> offset >> std::dec >> device >> inode; std::getline(tokens >> std::ws, uri_file_path); if (inode == 0 || uri_file_path.empty()) { return ret_value; } *fname_ptr = uri_file_path; *foffset_ptr = offset + address - low_address; ret_value = true; break; } } return ret_value; } bool Os::MemoryMapFileDesc(FileDesc fdesc, size_t fsize, size_t foffset, const void** mmap_ptr) { if (fdesc <= 0) { return false; } // If the offset is not aligned then align it // and recalculate the new size if (foffset > 0) { size_t old_foffset = foffset; foffset = alignUp(foffset, pageSize()); fsize += (foffset - old_foffset); } *mmap_ptr = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fdesc, foffset); return (*mmap_ptr == MAP_FAILED) ? false : true; } bool Os::MemoryUnmapFile(const void* mmap_ptr, size_t mmap_size) { if (munmap(const_cast(mmap_ptr), mmap_size) != 0) { return false; } return true; } bool Os::MemoryMapFile(const char* fname, const void** mmap_ptr, size_t* mmap_size) { if ((mmap_ptr == nullptr) || (mmap_size == nullptr)) { return false; } struct stat stat_buf; int fd = open(fname, O_RDONLY); if (fd < 0) { return false; } if (fstat(fd, &stat_buf) != 0) { close(fd); return false; } *mmap_size = stat_buf.st_size; *mmap_ptr = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0); close(fd); if (*mmap_ptr == MAP_FAILED) { return false; } return true; } bool Os::MemoryMapFileTruncated(const char* fname, const void** mmap_ptr, size_t mmap_size) { if (mmap_ptr == nullptr) { return false; } struct stat stat_buf; int fd = shm_open(fname, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO); if (fd < 0) { return false; } if (ftruncate(fd, mmap_size) != 0) { return false; } *mmap_ptr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); if (*mmap_ptr == MAP_FAILED) { return false; } return true; } int Os::getProcessId() { return ::getpid(); } // ================================================================================================ void* Os::CreateIpcMemory(const char* fname, size_t size, FileDesc* desc) { *desc = shm_open(fname, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO); if (*desc < 0) { return nullptr; } int status = ftruncate(*desc, size); if (status != 0) { return nullptr; } auto addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, *desc, 0); return addr; } // ================================================================================================ void* Os::OpenIpcMemory(const char* fname, const FileDesc desc, size_t size) { FileDesc handle = desc; if (fname != nullptr) { handle = shm_open(fname, O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO); } if (handle < 0) { return nullptr; } auto addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, handle, 0); return addr; } // ================================================================================================ void Os::CloseIpcMemory(const FileDesc desc, const void* ptr, size_t size) { if (ptr != nullptr) { munmap(const_cast(ptr), size); } if (desc != 0) { close(desc); } } // ================================================================================================ void Os::PrintLibraryLocation() { Dl_info dl_info; if (dladdr(reinterpret_cast(Os::loadLibrary), &dl_info) && dl_info.dli_fname) { ClPrint(amd::LOG_INFO, amd::LOG_INIT, "Runtime Library Path: %s", dl_info.dli_fname); } else { ClPrint(amd::LOG_INFO, amd::LOG_INIT, "Runtime Library Path: "); } } // ================================================================================================ bool Os::DumpCoreFile() { // Execute the default handler if a GPU core file should be generated ... struct rlimit rlimit; return (getrlimit(RLIMIT_CORE, &rlimit) == 0 && rlimit.rlim_cur != 0); } // ================================================================================================ void Os::CxaDemangle(const std::string& name, std::string* result) { int status = 0; char* demangled = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status); *result = (status == 0 && demangled != nullptr) ? demangled : name; free(demangled); } namespace numa { // ================================================================================================ uint32_t getCurrentNumaNode() { unsigned cpu, node; if (syscall(__NR_getcpu, &cpu, &node, nullptr) < 0) { return static_cast(-1); } return static_cast(node); } // ================================================================================================ NumaPolicy::NumaPolicy(const uint32_t numa_node_count) : node_map_((numa_node_count + kBitsPerUInt64 - 1) / kBitsPerUInt64, 0) { } // ================================================================================================ bool NumaPolicy::GetMemPolicy() { int policy = 0; if (syscall(__NR_get_mempolicy, &policy, node_map_.data(), node_map_.size() * kBitsPerUInt64, nullptr, 0) < 0) { ClPrint(amd::LOG_DEBUG, amd::LOG_RESOURCE, "syscall(__NR_get_mempolicy, size=%zu) failed to query policy", node_map_.size() * kBitsPerUInt64); return false; } if (policy < static_cast(Policy::kDefault) || policy > static_cast(Policy::kMax)) { ClPrint(amd::LOG_DEBUG, amd::LOG_RESOURCE, "syscall(__NR_get_mempolicy) returned wrong policy %d", policy); return false; } policy_ = static_cast(policy); return true; } // ================================================================================================ bool NumaPolicy::IsPolicySetAt(uint32_t node_index) const { const uint32_t i = node_index / kBitsPerUInt64; if (i < node_map_.size()) { return ((node_map_[i] >> (node_index % kBitsPerUInt64)) & 1) ? true: false; } else { return false; } } // ================================================================================================ NumaNode::~NumaNode() { if (affinity_) { delete static_cast *>(affinity_); affinity_ = nullptr; } } // ================================================================================================ bool NumaNode::GetAffinity() { const std::string path = "/sys/devices/system/node/node" + std::to_string(node_index_) + "/cpumap"; std::ifstream file(path); if (!file) { ClPrint(amd::LOG_DEBUG, amd::LOG_RESOURCE, "%s cannot be opened", path.c_str()); return false; } std::string line; std::getline(file, line); file.close(); // To remove commas and whitespace line.erase(std::remove_if(line.begin(), line.end(), [](unsigned char x) { return std::isspace(x) || x == ','; }), line.end()); constexpr uint32_t kHexsPerUInt64 = 2 * sizeof(uint64_t); auto affinity = new std::vector((line.size() + kHexsPerUInt64 - 1) / kHexsPerUInt64); auto iter = affinity->begin(); // To parse from the end (little-endian layout) for (int i = line.size(); i > 0; i -= kHexsPerUInt64) { uint32_t start = (i >= kHexsPerUInt64) ? i - kHexsPerUInt64 : 0; uint32_t len = (i >= kHexsPerUInt64) ? kHexsPerUInt64 : i; const std::string chunk = line.substr(start, len); const uint64_t value = std::stoul(chunk, nullptr, 16); *(iter++) = value; if (len == kHexsPerUInt64) { size_ += kBitsPerUInt64; } else { // Last one size_ = kBitsPerUInt64 - __builtin_clzl(value); } } affinity_ = affinity; return true; } // ================================================================================================ bool NumaNode::SchedSetAffinity() { if (!GetAffinity()) { return false; } if (syscall(__NR_sched_setaffinity, 0, size_, static_cast*>(affinity_)->data()) < 0) { ClPrint(amd::LOG_DEBUG, amd::LOG_RESOURCE, "syscall(__NR_sched_setaffinity, size=%u) failed", size_); return false; } return true; } // ================================================================================================ bool NumaNode::SchedSetAffinityIfAllowed() { ThreadAffinityState* affinity_state = GetThreadAffinityStatePtr(); const bool had_affinity_state = (affinity_state != nullptr); if (affinity_state != nullptr && affinity_state->runtime_set_affinity_mask_valid && affinity_state->runtime_set_affinity_node == node_index_) { return true; } std::vector current_affinity; if (!GetCurrentThreadAffinity(¤t_affinity)) { return false; } const bool current_mask_is_runtime_set = affinity_state != nullptr && affinity_state->runtime_set_affinity_mask_valid && IsSameAffinity(current_affinity, affinity_state->runtime_set_affinity_mask); if (!current_mask_is_runtime_set && !IsUnrestrictedAffinity(current_affinity)) { // Respect application, launcher, or cpuset affinity masks. return false; } affinity_state = &GetThreadAffinityState(); if (!affinity_state->original_affinity_mask_valid) { affinity_state->original_affinity_mask = current_affinity; affinity_state->original_affinity_mask_valid = true; } if (!SchedSetAffinity()) { if (!had_affinity_state) { ReleaseThreadAffinityState(); } return false; } affinity_state->runtime_set_affinity_mask_valid = GetCurrentThreadAffinity(&affinity_state->runtime_set_affinity_mask); if (affinity_state->runtime_set_affinity_mask_valid) { affinity_state->runtime_set_affinity_node = node_index_; } else { SetCurrentThreadAffinity(affinity_state->original_affinity_mask); ReleaseThreadAffinityState(); return false; } return true; } // ================================================================================================ bool resetThreadAffinity() { ThreadAffinityState* affinity_state = GetThreadAffinityStatePtr(); if (affinity_state == nullptr || !affinity_state->runtime_set_affinity_mask_valid || !affinity_state->original_affinity_mask_valid) { return false; } std::vector current_affinity; if (!GetCurrentThreadAffinity(¤t_affinity)) { return false; } if (!IsSameAffinity(current_affinity, affinity_state->runtime_set_affinity_mask)) { // The application changed affinity after ROCclr. Leave it intact. ReleaseThreadAffinityState(); return false; } const bool restored = SetCurrentThreadAffinity(affinity_state->original_affinity_mask); ReleaseThreadAffinityState(); return restored; } } // namespace numa } // namespace amd #endif // !defined(_WIN32) && !defined(__CYGWIN__)