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 std::string OSUtil::getCurrentDSODir() {
198  char Path[MAX_PATH];
199  Path[0] = '\0';
200  Path[sizeof(Path) - 1] = '\0';
201  auto Handle = getOSModuleHandle(reinterpret_cast<void *>(&getCurrentDSODir));
202  DWORD Ret = GetModuleFileNameA(
203  reinterpret_cast<HMODULE>(OSUtil::ExeModuleHandle == Handle ? 0 : Handle),
204  reinterpret_cast<LPSTR>(&Path), sizeof(Path));
205  assert(Ret < sizeof(Path) && "Path is longer than PATH_MAX?");
206  assert(Ret > 0 && "GetModuleFileNameA failed");
207  (void)Ret;
208 
209  BOOL RetCode = PathRemoveFileSpecA(reinterpret_cast<LPSTR>(&Path));
210  assert(RetCode && "PathRemoveFileSpecA failed");
211  (void)RetCode;
212 
213  return Path;
214 }
215 
216 std::string OSUtil::getDirName(const char *Path) {
217  std::string Tmp(Path);
218  // Remove trailing directory separators
219  Tmp.erase(Tmp.find_last_not_of("/\\") + 1, std::string::npos);
220 
221  size_t pos = Tmp.find_last_of("/\\");
222  if (pos != std::string::npos)
223  return Tmp.substr(0, pos);
224 
225  // If no directory separator is present return initial path like dirname does
226  return Tmp;
227 }
228 
229 #elif defined(__SYCL_RT_OS_DARWIN)
230 OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
231  Dl_info Res;
232  dladdr(VirtAddr, &Res);
233  return reinterpret_cast<OSModuleHandle>(Res.dli_fbase);
234 }
235 
236 std::string OSUtil::getCurrentDSODir() {
237  auto CurrentFunc = reinterpret_cast<const void *>(&getCurrentDSODir);
238  Dl_info Info;
239  int RetCode = dladdr(CurrentFunc, &Info);
240  if (0 == RetCode) {
241  // This actually indicates an error
242  return "";
243  }
244 
245  auto Path = std::string(Info.dli_fname);
246  auto LastSlashPos = Path.find_last_of('/');
247 
248  return Path.substr(0, LastSlashPos);
249 }
250 
251 #endif // __SYCL_RT_OS
252 
253 size_t OSUtil::getOSMemSize() {
254 #if defined(__SYCL_RT_OS_LINUX)
255  struct sysinfo MemInfo;
256  sysinfo(&MemInfo);
257  return static_cast<size_t>(MemInfo.totalram * MemInfo.mem_unit);
258 #elif defined(__SYCL_RT_OS_WINDOWS)
259  MEMORYSTATUSEX MemInfo;
260  MemInfo.dwLength = sizeof(MemInfo);
261  GlobalMemoryStatusEx(&MemInfo);
262  return static_cast<size_t>(MemInfo.ullTotalPhys);
263 #elif defined(__SYCL_RT_OS_DARWIN)
264  int64_t Size = 0;
265  sysctlbyname("hw.memsize", &Size, nullptr, nullptr, 0);
266  return static_cast<size_t>(Size);
267 #endif // __SYCL_RT_OS
268 }
269 
270 void *OSUtil::alignedAlloc(size_t Alignment, size_t NumBytes) {
271 #if defined(__SYCL_RT_OS_LINUX) && (defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) || \
272  defined(_LIBCPP_HAS_C11_FEATURES))
273  return aligned_alloc(Alignment, NumBytes);
274 #elif defined(__SYCL_RT_OS_POSIX_SUPPORT)
275  void *Addr = nullptr;
276  int ReturnCode = posix_memalign(&Addr, Alignment, NumBytes);
277  return (ReturnCode == 0) ? Addr : nullptr;
278 #elif defined(__SYCL_RT_OS_WINDOWS)
279  return _aligned_malloc(NumBytes, Alignment);
280 #endif
281 }
282 
283 void OSUtil::alignedFree(void *Ptr) {
284 #if defined(__SYCL_RT_OS_LINUX) || defined(__SYCL_RT_OS_POSIX_SUPPORT)
285  free(Ptr);
286 #elif defined(__SYCL_RT_OS_WINDOWS)
287  _aligned_free(Ptr);
288 #endif
289 }
290 
291 /* This is temporary solution until std::filesystem is available when SYCL RT
292  * is moved to c++17 standard*/
293 
294 /* Create directory recursively and return non zero code on success*/
295 int OSUtil::makeDir(const char *Dir) {
296  assert((Dir != nullptr) && "Passed null-pointer as directory name.");
297  if (isPathPresent(Dir))
298  return 0;
299 
300  std::string Path{Dir}, CurPath;
301  size_t pos = 0;
302 
303  do {
304  pos = Path.find_first_of("/\\", ++pos);
305  CurPath = Path.substr(0, pos);
306 #if defined(__SYCL_RT_OS_POSIX_SUPPORT)
307  auto Res = mkdir(CurPath.c_str(), 0777);
308 #else
309  auto Res = _mkdir(CurPath.c_str());
310 #endif
311  if (Res && errno != EEXIST)
312  return Res;
313  } while (pos != std::string::npos);
314  return 0;
315 }
316 
317 } // namespace detail
318 } // __SYCL_INLINE_VER_NAMESPACE(_V1)
319 } // namespace sycl
#define __SYCL_INLINE_VER_NAMESPACE(X)
void * alignedAlloc(size_t Alignment, size_t Bytes, const context &Ctxt, const device &Dev, sycl::usm::alloc Kind, const code_location &CL)
void free(void *Ptr, const context &Ctxt, const code_location &CL)
Definition: usm_impl.cpp:221
std::string getDirName(const char *Path)
conditional< sizeof(long)==8, long, long long >::type int64_t
Definition: kernel_desc.hpp:34
intptr_t OSModuleHandle
Uniquely identifies an operating system module (executable or a dynamic library)
Definition: os_util.hpp:48
void * aligned_alloc(size_t alignment, size_t size, const device &dev, const context &ctxt, usm::alloc kind _CODELOCPARAM(&CodeLoc))
---— Error handling, matching OpenCL plugin semantics.
Definition: access.hpp:14
simd< _Tp, _Abi > max(const simd< _Tp, _Abi > &, const simd< _Tp, _Abi > &) noexcept