DPC++ Runtime
Runtime libraries for oneAPI DPC++
os_util.cpp
Go to the documentation of this file.
1 //===-- os_util.cpp - OS utilities implementation---------------*- C++ -*--===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include <sycl/exception.hpp>
11 
12 #include <cassert>
13 
14 #if defined(__SYCL_RT_OS_LINUX)
15 
16 #ifndef _GNU_SOURCE
17 #define _GNU_SOURCE
18 #endif // _GNU_SOURCE
19 
20 #include <cstdio>
21 #include <cstring>
22 #include <dlfcn.h>
23 #include <fstream>
24 #include <libgen.h> // for dirname
25 #include <link.h>
26 #include <linux/limits.h> // for PATH_MAX
27 #include <sys/stat.h>
28 #include <sys/sysinfo.h>
29 
30 #elif defined(__SYCL_RT_OS_WINDOWS)
31 
32 #include <Windows.h>
33 #include <direct.h>
34 #include <malloc.h>
35 #include <shlwapi.h>
36 
37 #elif defined(__SYCL_RT_OS_DARWIN)
38 
39 #include <dlfcn.h>
40 #include <sys/sysctl.h>
41 #include <sys/types.h>
42 
43 #endif // __SYCL_RT_OS
44 
45 namespace sycl {
47 namespace detail {
48 
49 #if defined(__SYCL_RT_OS_LINUX)
50 
51 struct ModuleInfo {
52  const void *VirtAddr; // in
53  void *Handle; // out
54  const char *Name; // out
55 };
56 
57 constexpr OSModuleHandle OSUtil::ExeModuleHandle;
58 constexpr OSModuleHandle OSUtil::DummyModuleHandle;
59 
60 static int callback(struct dl_phdr_info *Info, size_t, void *Data) {
61  auto Base = reinterpret_cast<unsigned char *>(Info->dlpi_addr);
62  auto MI = reinterpret_cast<ModuleInfo *>(Data);
63  auto TestAddr = reinterpret_cast<const unsigned char *>(MI->VirtAddr);
64 
65  for (int i = 0; i < Info->dlpi_phnum; ++i) {
66  unsigned char *SegStart = Base + Info->dlpi_phdr[i].p_vaddr;
67  unsigned char *SegEnd = SegStart + Info->dlpi_phdr[i].p_memsz;
68 
69  // check if the tested address is within current segment
70  if (TestAddr >= SegStart && TestAddr < SegEnd) {
71  // ... it is - belongs to the module then
72  // dlpi_addr is zero for the executable, replace it
73  auto H = reinterpret_cast<void *>(Info->dlpi_addr);
74  MI->Handle = H ? H : reinterpret_cast<void *>(OSUtil::ExeModuleHandle);
75  MI->Name = Info->dlpi_name;
76  return 1; // non-zero tells to finish iteration via modules
77  }
78  }
79  return 0;
80 }
81 
82 OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
83  ModuleInfo Res = {VirtAddr, nullptr, nullptr};
84  dl_iterate_phdr(callback, &Res);
85 
86  return reinterpret_cast<OSModuleHandle>(Res.Handle);
87 }
88 
89 bool procMapsAddressInRange(std::istream &Stream, uintptr_t Addr) {
90  uintptr_t Start = 0, End = 0;
91  Stream >> Start;
92  assert(!Stream.fail() && Stream.peek() == '-' &&
93  "Couldn't read /proc/self/maps correctly");
94  Stream.ignore(1);
95 
96  Stream >> End;
97  assert(!Stream.fail() && Stream.peek() == ' ' &&
98  "Couldn't read /proc/self/maps correctly");
99  Stream.ignore(1);
100 
101  return Addr >= Start && Addr < End;
102 }
103 
105 std::string OSUtil::getCurrentDSODir() {
106  // Examine /proc/self/maps and find where this function (getCurrendDSODir)
107  // comes from - this is supposed to be an absolute path to libsycl.so.
108  //
109  // File structure is the following:
110  // address perms offset dev inode pathname
111  // 00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/foo
112  // 007c2000-007c8000 r--p 001c2000 fc:05 52567930 /usr/bin/bar
113  //
114  // We need to:
115  //
116  // 1) Iterate over lines and find the line which have an address of the
117  // current function in an `address' range.
118  //
119  // 2) Check that perms have read and executable flags (since we do execute
120  // this function).
121  //
122  // 3) Skip offset, dev, inode
123  //
124  // 4) Extract an absolute path to a filename and get a dirname from it.
125  //
126  uintptr_t CurrentFunc = (uintptr_t)&getCurrentDSODir;
127  std::ifstream Stream("/proc/self/maps");
128  Stream >> std::hex;
129  while (!Stream.eof()) {
130  if (!procMapsAddressInRange(Stream, CurrentFunc)) {
131  // Skip the rest until an EOL and check the next line
132  Stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
133  continue;
134  }
135 
136  char Perm[4];
137  Stream.readsome(Perm, sizeof(Perm));
138  assert(Perm[0] == 'r' && Perm[2] == 'x' &&
139  "Invalid flags in /proc/self/maps");
140  assert(Stream.peek() == ' ');
141  Stream.ignore(1);
142 
143  // Read and ignore the following:
144  // offset
145  Stream.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
146  Stream.ignore(1);
147  // dev major
148  Stream.ignore(std::numeric_limits<std::streamsize>::max(), ':');
149  Stream.ignore(1);
150  // dev minor
151  Stream.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
152  Stream.ignore(1);
153  // inode
154  Stream.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
155  Stream.ignore(1);
156 
157  // Now read the path: it is padded with whitespaces, so we skip them
158  // first.
159  while (Stream.peek() == ' ') {
160  Stream.ignore(1);
161  }
162  char Path[PATH_MAX];
163  Stream.getline(Path, PATH_MAX - 1);
164  Path[PATH_MAX - 1] = '\0';
165  return OSUtil::getDirName(Path);
166  }
167  assert(false && "Unable to find the current function in /proc/self/maps");
168  return "";
169 }
170 
171 std::string OSUtil::getDirName(const char *Path) {
172  std::string Tmp(Path);
173  // dirname(3) needs a writable C string: a null-terminator is written where a
174  // path should split.
175  size_t TruncatedSize = strlen(dirname(const_cast<char *>(Tmp.c_str())));
176  Tmp.resize(TruncatedSize);
177  return Tmp;
178 }
179 
180 #elif defined(__SYCL_RT_OS_WINDOWS)
181 OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
182  HMODULE PhModule;
183  DWORD Flag = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
184  GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
185  auto LpModuleAddr = reinterpret_cast<LPCSTR>(VirtAddr);
186  if (!GetModuleHandleExA(Flag, LpModuleAddr, &PhModule)) {
187  // Expect the caller to check for zero and take
188  // necessary action
189  return 0;
190  }
191  if (PhModule == GetModuleHandleA(nullptr))
192  return OSUtil::ExeModuleHandle;
193  return reinterpret_cast<OSModuleHandle>(PhModule);
194 }
195 
197 // win_proxy_loader.dll uses this same logic. If it is changed
198 // significantly, it might be wise to change it there too.
199 std::string OSUtil::getCurrentDSODir() {
200  char Path[MAX_PATH];
201  Path[0] = '\0';
202  Path[sizeof(Path) - 1] = '\0';
203  auto Handle = getOSModuleHandle(reinterpret_cast<void *>(&getCurrentDSODir));
204  DWORD Ret = GetModuleFileNameA(
205  reinterpret_cast<HMODULE>(OSUtil::ExeModuleHandle == Handle ? 0 : Handle),
206  reinterpret_cast<LPSTR>(&Path), sizeof(Path));
207  assert(Ret < sizeof(Path) && "Path is longer than PATH_MAX?");
208  assert(Ret > 0 && "GetModuleFileNameA failed");
209  (void)Ret;
210 
211  BOOL RetCode = PathRemoveFileSpecA(reinterpret_cast<LPSTR>(&Path));
212  assert(RetCode && "PathRemoveFileSpecA failed");
213  (void)RetCode;
214 
215  return Path;
216 }
217 
218 std::string OSUtil::getDirName(const char *Path) {
219  std::string Tmp(Path);
220  // Remove trailing directory separators
221  Tmp.erase(Tmp.find_last_not_of("/\\") + 1, std::string::npos);
222 
223  size_t pos = Tmp.find_last_of("/\\");
224  if (pos != std::string::npos)
225  return Tmp.substr(0, pos);
226 
227  // If no directory separator is present return initial path like dirname does
228  return Tmp;
229 }
230 
231 #elif defined(__SYCL_RT_OS_DARWIN)
232 OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
233  Dl_info Res;
234  dladdr(VirtAddr, &Res);
235  return reinterpret_cast<OSModuleHandle>(Res.dli_fbase);
236 }
237 
238 std::string OSUtil::getCurrentDSODir() {
239  auto CurrentFunc = reinterpret_cast<const void *>(&getCurrentDSODir);
240  Dl_info Info;
241  int RetCode = dladdr(CurrentFunc, &Info);
242  if (0 == RetCode) {
243  // This actually indicates an error
244  return "";
245  }
246 
247  auto Path = std::string(Info.dli_fname);
248  auto LastSlashPos = Path.find_last_of('/');
249 
250  return Path.substr(0, LastSlashPos);
251 }
252 
253 #endif // __SYCL_RT_OS
254 
255 size_t OSUtil::getOSMemSize() {
256 #if defined(__SYCL_RT_OS_LINUX)
257  struct sysinfo MemInfo;
258  sysinfo(&MemInfo);
259  return static_cast<size_t>(MemInfo.totalram * MemInfo.mem_unit);
260 #elif defined(__SYCL_RT_OS_WINDOWS)
261  MEMORYSTATUSEX MemInfo;
262  MemInfo.dwLength = sizeof(MemInfo);
263  GlobalMemoryStatusEx(&MemInfo);
264  return static_cast<size_t>(MemInfo.ullTotalPhys);
265 #elif defined(__SYCL_RT_OS_DARWIN)
266  int64_t Size = 0;
267  sysctlbyname("hw.memsize", &Size, nullptr, nullptr, 0);
268  return static_cast<size_t>(Size);
269 #endif // __SYCL_RT_OS
270 }
271 
272 void *OSUtil::alignedAlloc(size_t Alignment, size_t NumBytes) {
273 #if defined(__SYCL_RT_OS_LINUX) && (defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) || \
274  defined(_LIBCPP_HAS_C11_FEATURES))
275  return aligned_alloc(Alignment, NumBytes);
276 #elif defined(__SYCL_RT_OS_POSIX_SUPPORT)
277  void *Addr = nullptr;
278  int ReturnCode = posix_memalign(&Addr, Alignment, NumBytes);
279  return (ReturnCode == 0) ? Addr : nullptr;
280 #elif defined(__SYCL_RT_OS_WINDOWS)
281  return _aligned_malloc(NumBytes, Alignment);
282 #endif
283 }
284 
285 void OSUtil::alignedFree(void *Ptr) {
286 #if defined(__SYCL_RT_OS_LINUX) || defined(__SYCL_RT_OS_POSIX_SUPPORT)
287  free(Ptr);
288 #elif defined(__SYCL_RT_OS_WINDOWS)
289  _aligned_free(Ptr);
290 #endif
291 }
292 
293 /* This is temporary solution until std::filesystem is available when SYCL RT
294  * is moved to c++17 standard*/
295 
296 /* Create directory recursively and return non zero code on success*/
297 int OSUtil::makeDir(const char *Dir) {
298  assert((Dir != nullptr) && "Passed null-pointer as directory name.");
299  if (isPathPresent(Dir))
300  return 0;
301 
302  std::string Path{Dir}, CurPath;
303  size_t pos = 0;
304 
305  do {
306  pos = Path.find_first_of("/\\", ++pos);
307  CurPath = Path.substr(0, pos);
308 #if defined(__SYCL_RT_OS_POSIX_SUPPORT)
309  auto Res = mkdir(CurPath.c_str(), 0777);
310 #else
311  auto Res = _mkdir(CurPath.c_str());
312 #endif
313  if (Res && errno != EEXIST)
314  return Res;
315  } while (pos != std::string::npos);
316  return 0;
317 }
318 
319 } // namespace detail
320 } // __SYCL_INLINE_VER_NAMESPACE(_V1)
321 } // namespace sycl
__SYCL_INLINE_VER_NAMESPACE
#define __SYCL_INLINE_VER_NAMESPACE(X)
Definition: defines_elementary.hpp:11
sycl::_V1::detail::int64_t
conditional< sizeof(long)==8, long, long long >::type int64_t
Definition: kernel_desc.hpp:34
os_util.hpp
sycl
---— Error handling, matching OpenCL plugin semantics.
Definition: access.hpp:14
max
simd< _Tp, _Abi > max(const simd< _Tp, _Abi > &, const simd< _Tp, _Abi > &) noexcept
sycl::_V1::aligned_alloc
void * aligned_alloc(size_t alignment, size_t size, const device &dev, const context &ctxt, usm::alloc kind _CODELOCPARAM(&CodeLoc))
sycl::_V1::ext::oneapi::experimental::detail::Alignment
@ Alignment
Definition: property.hpp:189
sycl::_V1::detail::OSModuleHandle
intptr_t OSModuleHandle
Uniquely identifies an operating system module (executable or a dynamic library)
Definition: os_util.hpp:48
sycl::_V1::detail::usm::alignedAlloc
void * alignedAlloc(size_t Alignment, size_t Bytes, const context &Ctxt, const device &Dev, sycl::usm::alloc Kind, const code_location &CL)
exception.hpp
sycl::_V1::detail::getDirName
std::string getDirName(const char *Path)
sycl::_V1::detail::usm::free
void free(void *Ptr, const context &Ctxt, const code_location &CL)
Definition: usm_impl.cpp:267