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 
11 #include <cassert>
12 #include <limits>
13 
14 #if __GNUC__ && __GNUC__ < 8
15 // Don't include <filesystem> for GCC versions less than 8
16 #else
17 #include <filesystem> // C++ 17 std::create_directories
18 #endif
19 
20 #if defined(__SYCL_RT_OS_LINUX)
21 
22 #ifndef _GNU_SOURCE
23 #define _GNU_SOURCE
24 #endif // _GNU_SOURCE
25 
26 #include <cstdio>
27 #include <cstring>
28 #include <dlfcn.h>
29 #include <fstream>
30 #include <libgen.h> // for dirname
31 #include <link.h>
32 #include <linux/limits.h> // for PATH_MAX
33 #include <sys/stat.h>
34 #include <sys/sysinfo.h>
35 
36 #elif defined(__SYCL_RT_OS_WINDOWS)
37 
39 
40 #include <Windows.h>
41 #include <malloc.h>
42 #include <shlwapi.h>
43 
44 #elif defined(__SYCL_RT_OS_DARWIN)
45 
46 #include <dlfcn.h>
47 #include <sys/sysctl.h>
48 #include <sys/types.h>
49 
50 #endif // __SYCL_RT_OS
51 
52 namespace sycl {
53 inline namespace _V1 {
54 namespace detail {
55 
56 #if defined(__SYCL_RT_OS_LINUX)
57 bool procMapsAddressInRange(std::istream &Stream, uintptr_t Addr) {
58  uintptr_t Start = 0, End = 0;
59  Stream >> Start;
60  assert(!Stream.fail() && Stream.peek() == '-' &&
61  "Couldn't read /proc/self/maps correctly");
62  Stream.ignore(1);
63 
64  Stream >> End;
65  assert(!Stream.fail() && Stream.peek() == ' ' &&
66  "Couldn't read /proc/self/maps correctly");
67  Stream.ignore(1);
68 
69  return Addr >= Start && Addr < End;
70 }
71 
73 std::string OSUtil::getCurrentDSODir() {
74  // Examine /proc/self/maps and find where this function (getCurrendDSODir)
75  // comes from - this is supposed to be an absolute path to libsycl.so.
76  //
77  // File structure is the following:
78  // address perms offset dev inode pathname
79  // 00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/foo
80  // 007c2000-007c8000 r--p 001c2000 fc:05 52567930 /usr/bin/bar
81  //
82  // We need to:
83  //
84  // 1) Iterate over lines and find the line which have an address of the
85  // current function in an `address' range.
86  //
87  // 2) Check that perms have read and executable flags (since we do execute
88  // this function).
89  //
90  // 3) Skip offset, dev, inode
91  //
92  // 4) Extract an absolute path to a filename and get a dirname from it.
93  //
94  uintptr_t CurrentFunc = (uintptr_t)&getCurrentDSODir;
95  std::ifstream Stream("/proc/self/maps");
96  Stream >> std::hex;
97  while (!Stream.eof()) {
98  if (!procMapsAddressInRange(Stream, CurrentFunc)) {
99  // Skip the rest until an EOL and check the next line
100  Stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
101  continue;
102  }
103 
104  char Perm[4];
105  Stream.readsome(Perm, sizeof(Perm));
106  assert(Perm[0] == 'r' && Perm[2] == 'x' &&
107  "Invalid flags in /proc/self/maps");
108  assert(Stream.peek() == ' ');
109  Stream.ignore(1);
110 
111  // Read and ignore the following:
112  // offset
113  Stream.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
114  Stream.ignore(1);
115  // dev major
116  Stream.ignore(std::numeric_limits<std::streamsize>::max(), ':');
117  Stream.ignore(1);
118  // dev minor
119  Stream.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
120  Stream.ignore(1);
121  // inode
122  Stream.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
123  Stream.ignore(1);
124 
125  // Now read the path: it is padded with whitespaces, so we skip them
126  // first.
127  while (Stream.peek() == ' ') {
128  Stream.ignore(1);
129  }
130  char Path[PATH_MAX];
131  Stream.getline(Path, PATH_MAX - 1);
132  Path[PATH_MAX - 1] = '\0';
133  return OSUtil::getDirName(Path);
134  }
135  assert(false && "Unable to find the current function in /proc/self/maps");
136  return "";
137 }
138 
139 std::string OSUtil::getDirName(const char *Path) {
140  std::string Tmp(Path);
141  // dirname(3) needs a writable C string: a null-terminator is written where a
142  // path should split.
143  size_t TruncatedSize = strlen(dirname(const_cast<char *>(Tmp.c_str())));
144  Tmp.resize(TruncatedSize);
145  return Tmp;
146 }
147 
148 #elif defined(__SYCL_RT_OS_WINDOWS)
149 
151 // pi_win_proxy_loader.dll uses this same logic. If it is changed
152 // significantly, it might be wise to change it there too.
153 std::string OSUtil::getCurrentDSODir() {
154  char Path[MAX_PATH];
155  Path[0] = '\0';
156  Path[sizeof(Path) - 1] = '\0';
157  auto Handle = getOSModuleHandle(reinterpret_cast<void *>(&getCurrentDSODir));
158  DWORD Ret = GetModuleFileNameA(
159  reinterpret_cast<HMODULE>(ExeModuleHandle == Handle ? 0 : Handle),
160  reinterpret_cast<LPSTR>(&Path), sizeof(Path));
161  assert(Ret < sizeof(Path) && "Path is longer than PATH_MAX?");
162  assert(Ret > 0 && "GetModuleFileNameA failed");
163  (void)Ret;
164 
165  BOOL RetCode = PathRemoveFileSpecA(reinterpret_cast<LPSTR>(&Path));
166  assert(RetCode && "PathRemoveFileSpecA failed");
167  (void)RetCode;
168 
169  return Path;
170 }
171 
172 std::string OSUtil::getDirName(const char *Path) {
173  std::string Tmp(Path);
174  // Remove trailing directory separators
175  Tmp.erase(Tmp.find_last_not_of("/\\") + 1, std::string::npos);
176 
177  size_t pos = Tmp.find_last_of("/\\");
178  if (pos != std::string::npos)
179  return Tmp.substr(0, pos);
180 
181  // If no directory separator is present return initial path like dirname does
182  return Tmp;
183 }
184 
185 #elif defined(__SYCL_RT_OS_DARWIN)
186 std::string OSUtil::getCurrentDSODir() {
187  auto CurrentFunc = reinterpret_cast<const void *>(&getCurrentDSODir);
188  Dl_info Info;
189  int RetCode = dladdr(CurrentFunc, &Info);
190  if (0 == RetCode) {
191  // This actually indicates an error
192  return "";
193  }
194 
195  auto Path = std::string(Info.dli_fname);
196  auto LastSlashPos = Path.find_last_of('/');
197 
198  return Path.substr(0, LastSlashPos);
199 }
200 
201 #endif // __SYCL_RT_OS
202 
204 #if defined(__SYCL_RT_OS_LINUX)
205  struct sysinfo MemInfo;
206  sysinfo(&MemInfo);
207  return static_cast<size_t>(MemInfo.totalram * MemInfo.mem_unit);
208 #elif defined(__SYCL_RT_OS_WINDOWS)
209  MEMORYSTATUSEX MemInfo;
210  MemInfo.dwLength = sizeof(MemInfo);
211  GlobalMemoryStatusEx(&MemInfo);
212  return static_cast<size_t>(MemInfo.ullTotalPhys);
213 #elif defined(__SYCL_RT_OS_DARWIN)
214  int64_t Size = 0;
215  sysctlbyname("hw.memsize", &Size, nullptr, nullptr, 0);
216  return static_cast<size_t>(Size);
217 #endif // __SYCL_RT_OS
218 }
219 
220 void *OSUtil::alignedAlloc(size_t Alignment, size_t NumBytes) {
221 #if defined(__SYCL_RT_OS_LINUX) && (defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) || \
222  defined(_LIBCPP_HAS_C11_FEATURES))
223  return aligned_alloc(Alignment, NumBytes);
224 #elif defined(__SYCL_RT_OS_POSIX_SUPPORT)
225  void *Addr = nullptr;
226  int ReturnCode = posix_memalign(&Addr, Alignment, NumBytes);
227  return (ReturnCode == 0) ? Addr : nullptr;
228 #elif defined(__SYCL_RT_OS_WINDOWS)
229  return _aligned_malloc(NumBytes, Alignment);
230 #endif
231 }
232 
233 void OSUtil::alignedFree(void *Ptr) {
234 #if defined(__SYCL_RT_OS_LINUX) || defined(__SYCL_RT_OS_POSIX_SUPPORT)
235  free(Ptr);
236 #elif defined(__SYCL_RT_OS_WINDOWS)
237  _aligned_free(Ptr);
238 #endif
239 }
240 
241 // Make all directories on the path, throws on error.
242 int OSUtil::makeDir(const char *Dir) {
243  assert((Dir != nullptr) && "Passed null-pointer as directory name.");
244  if (isPathPresent(Dir))
245  return 0;
246 
247 // older GCC doesn't have full C++ 17 support.
248 #if __GNUC__ && __GNUC__ < 8
249  std::string Path{Dir}, CurPath;
250  size_t pos = 0;
251 
252  do {
253  pos = Path.find_first_of("/\\", ++pos);
254  CurPath = Path.substr(0, pos);
255 #if defined(__SYCL_RT_OS_POSIX_SUPPORT)
256  auto Res = mkdir(CurPath.c_str(), 0777);
257 #else
258  auto Res = _mkdir(CurPath.c_str());
259 #endif
260  if (Res && errno != EEXIST)
261  throw std::runtime_error("Failed to mkdir: " + CurPath + " (" +
262  std::strerror(errno) + ")");
263 
264  } while (pos != std::string::npos);
265 #else
266  // using filesystem is simpler, more reliable, works better on Win
267  std::filesystem::path path(Dir);
268  std::filesystem::create_directories(path.make_preferred());
269 #endif
270  return 0;
271 }
272 
273 } // namespace detail
274 } // namespace _V1
275 } // namespace sycl
static void * alignedAlloc(size_t Alignment, size_t NumBytes)
Allocates NumBytes bytes of uninitialized storage whose alignment is specified by Alignment.
Definition: os_util.cpp:220
static int makeDir(const char *Dir)
Make all directories on the path, throws on error.
Definition: os_util.cpp:242
static void alignedFree(void *Ptr)
Deallocates the memory referenced by Ptr.
Definition: os_util.cpp:233
static bool isPathPresent(const std::string &Path)
Checks if specified path is present.
Definition: os_util.hpp:72
static size_t getOSMemSize()
Returns the amount of RAM available for the operating system.
Definition: os_util.cpp:203
static std::string getDirName(const char *Path)
Returns a directory component of a path.
static std::string getCurrentDSODir()
Returns an absolute path to a directory where the object was found.
conditional< sizeof(long)==8, long, long long >::type int64_t
Definition: kernel_desc.hpp:35
void * aligned_alloc(size_t alignment, size_t size, const device &dev, const context &ctxt, usm::alloc kind, const detail::code_location &CodeLoc=detail::code_location::current())
void free(void *ptr, const context &ctxt, const detail::code_location &CodeLoc=detail::code_location::current())
Definition: usm_impl.cpp:374
Definition: access.hpp:18
OSModuleHandle getOSModuleHandle(const void *VirtAddr)
constexpr OSModuleHandle ExeModuleHandle