Skip to content

Commit 27700f6

Browse files
author
Andrew Savonichev
committed
[SYCL] Add OSUtil::getCurrentDSODir() function
The function returns a directory where the current dynamic shared object (libsycl.so or sycl.dll in our case) was found. This can be useful to access files relative to the libsycl.so directory: config files, supplemental libraries, etc. Signed-off-by: Andrew Savonichev <[email protected]>
1 parent 20248cf commit 27700f6

File tree

6 files changed

+187
-0
lines changed

6 files changed

+187
-0
lines changed

sycl/include/CL/sycl/detail/os_util.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include <cstdint>
1414
#include <stdlib.h>
15+
#include <string>
1516

1617
#ifdef _WIN32
1718
#define SYCL_RT_OS_WINDOWS
@@ -67,10 +68,22 @@ class OSUtil {
6768
/// Returns a module enclosing given address or nullptr.
6869
static OSModuleHandle getOSModuleHandle(const void *VirtAddr);
6970

71+
/// Returns an absolute path to a directory where the object was found.
72+
static std::string getCurrentDSODir();
73+
74+
/// Returns a directory component of a path.
75+
static std::string getDirName(const char* Path);
76+
7077
/// Module handle for the executable module - it is assumed there is always
7178
/// single one at most.
7279
static constexpr OSModuleHandle ExeModuleHandle = -1;
7380

81+
#ifdef SYCL_RT_OS_WINDOWS
82+
static constexpr const char* DirSep = "\\";
83+
#else
84+
static constexpr const char* DirSep = "/";
85+
#endif
86+
7487
/// Returns the amount of RAM available for the operating system.
7588
static size_t getOSMemSize();
7689

sycl/source/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ function(add_sycl_rt_library LIB_NAME)
1919

2020
if (MSVC)
2121
target_compile_definitions(${LIB_NAME} PRIVATE __SYCL_BUILD_SYCL_DLL )
22+
target_link_libraries(${LIB_NAME} PRIVATE shlwapi)
2223
endif()
2324
target_include_directories(${LIB_NAME} PRIVATE "${sycl_inc_dir}")
2425
target_link_libraries(${LIB_NAME}

sycl/source/detail/os_util.cpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@
99
#include <CL/sycl/detail/os_util.hpp>
1010
#include <CL/sycl/exception.hpp>
1111

12+
#include <cassert>
13+
1214
#ifdef SYCL_RT_OS_POSIX_SUPPORT
1315
#include <cstdlib>
16+
#include <cstring>
17+
#include <fstream>
1418
#endif
1519

1620
#if defined(SYCL_RT_OS_LINUX)
@@ -20,13 +24,19 @@
2024
#endif // _GNU_SOURCE
2125

2226
#include <cstdio>
27+
#include <dlfcn.h>
28+
#include <libgen.h> // for dirname
29+
#include <link.h>
2330
#include <link.h>
31+
#include <linux/limits.h> // for PATH_MAX
32+
#include <stdio.h>
2433
#include <sys/sysinfo.h>
2534

2635
#elif defined(SYCL_RT_OS_WINDOWS)
2736

2837
#include <Windows.h>
2938
#include <malloc.h>
39+
#include <shlwapi.h>
3040

3141
#elif defined(SYCL_RT_OS_DARWIN)
3242

@@ -77,6 +87,99 @@ OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
7787
return reinterpret_cast<OSModuleHandle>(Res.Handle);
7888
}
7989

90+
bool procMapsAddressInRange(std::istream &Stream, uintptr_t Addr) {
91+
uintptr_t Start, End;
92+
Stream >> Start;
93+
assert(!Stream.fail() && Stream.peek() == '-' &&
94+
"Couldn't read /proc/self/maps correctly");
95+
Stream.ignore(1);
96+
97+
Stream >> End;
98+
assert(!Stream.fail() && Stream.peek() == ' ' &&
99+
"Couldn't read /proc/self/maps correctly");
100+
Stream.ignore(1);
101+
102+
return Addr >= Start && Addr < End;
103+
}
104+
105+
/// Returns an absolute path to a directory where the object was found.
106+
std::string OSUtil::getCurrentDSODir() {
107+
// Examine /proc/self/maps and find where ^~~this function comes from - this
108+
// is supposed to be an absolute path to libsycl.so.
109+
//
110+
// File structure is the following:
111+
// address perms offset dev inode pathname
112+
// 00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/foo
113+
// 007c2000-007c8000 r--p 001c2000 fc:05 52567930 /usr/bin/bar
114+
//
115+
// We need to:
116+
//
117+
// 1) Iterate over lines and find the line which have an address of the
118+
// current function in an `address' range.
119+
//
120+
// 2) Check that perms have read and executable flags (since we do execute
121+
// this function).
122+
//
123+
// 3) Skip offset, dev, inode
124+
//
125+
// 4) Extract an absolute path to a filename and get a dirname from it.
126+
//
127+
uintptr_t CurrentFunc = (uintptr_t) &getCurrentDSODir;
128+
std::ifstream Stream("/proc/self/maps");
129+
Stream >> std::hex;
130+
while (!Stream.eof()) {
131+
if (!procMapsAddressInRange(Stream, CurrentFunc)) {
132+
// Skip the rest until an EOL and check the next line
133+
Stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
134+
continue;
135+
}
136+
137+
char Perm[4];
138+
Stream.readsome(Perm, sizeof(Perm));
139+
assert(Perm[0] == 'r' && Perm[2] == 'x' &&
140+
"Invalid flags in /proc/self/maps");
141+
assert(Stream.peek() == ' ');
142+
Stream.ignore(1);
143+
144+
// Read and ignore the following:
145+
uintptr_t Offset, DevMajor, DevMinor, Inode;
146+
Stream >> Offset;
147+
assert(Stream.peek() == ' ');
148+
Stream.ignore(1);
149+
150+
Stream >> DevMajor;
151+
assert(Stream.peek() == ':' &&
152+
"Couldn't read dev field in /proc/self/maps");
153+
Stream.ignore(1);
154+
Stream >> DevMinor;
155+
assert(Stream.peek() == ' ');
156+
Stream.ignore(1);
157+
158+
Stream >> Inode;
159+
assert(Stream.peek() == ' ');
160+
Stream.ignore(1);
161+
162+
// Now read the path: it is padded with whitespaces, so we skip them
163+
// first.
164+
while (Stream.peek() == ' ') {
165+
Stream.ignore(1);
166+
}
167+
char Path[PATH_MAX];
168+
Stream.getline(Path, PATH_MAX - 1);
169+
Path[PATH_MAX - 1] = '\0';
170+
return OSUtil::getDirName(Path);
171+
}
172+
assert(false && "Unable to find the current function in /proc/self/maps");
173+
return "";
174+
}
175+
176+
std::string OSUtil::getDirName(const char* Path) {
177+
std::string Tmp(Path);
178+
// dirname(3) needs a writable C string
179+
return std::string(
180+
dirname(const_cast<char*>(Tmp.c_str())));
181+
}
182+
80183
#elif defined(SYCL_RT_OS_WINDOWS)
81184
OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
82185
HMODULE PhModule;
@@ -93,6 +196,25 @@ OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
93196
return reinterpret_cast<OSModuleHandle>(PhModule);
94197
}
95198

199+
/// Returns an absolute path where the object was found.
200+
std::string OSUtil::getCurrentDSODir() {
201+
char Path[MAX_PATH];
202+
Path[0] = '\0';
203+
Path[sizeof(Path) - 1] = '\0';
204+
DWORD Ret = GetModuleFileNameA(
205+
reinterpret_cast<HMODULE>(getOSModuleHandle(&getCurrentDSODir)),
206+
reinterpret_cast<LPSTR>(&Path),
207+
sizeof(Path));
208+
assert(Ret < sizeof(Path) && "Path is longer than PATH_MAX?");
209+
assert(Ret > 0 && "GetModuleFileNameA failed");
210+
211+
BOOL RetCode = PathRemoveFileSpecA(reinterpret_cast<LPSTR>(&Path));
212+
assert(RetCode && "PathRemoveFileSpecA failed");
213+
(void)RetCode;
214+
215+
return Path;
216+
};
217+
96218
#elif defined(SYCL_RT_OS_DARWIN)
97219
OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
98220
Dl_info Res;

sycl/unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ function(add_sycl_unittest test_dirname)
2727
endfunction()
2828

2929
add_subdirectory(pi)
30+
add_subdirectory(misc)

sycl/unittests/misc/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
set(CMAKE_CXX_EXTENSIONS OFF)
2+
3+
set(sycl_lib_dir $<TARGET_FILE_DIR:sycl>)
4+
add_definitions(-DSYCL_LIB_DIR="${sycl_lib_dir}")
5+
add_sycl_unittest(MiscTests
6+
OsUtils.cpp
7+
)

sycl/unittests/misc/OsUtils.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//==---- OsUtils.cpp --- os_utils unit test --------------------------------==//
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+
9+
#include <CL/sycl/detail/os_util.hpp>
10+
#include <gtest/gtest.h>
11+
12+
#ifdef __unix__
13+
#include <sys/stat.h>
14+
#include <stdlib.h>
15+
/// Check with respect to symbolic links
16+
bool isSameDir(const char* LHS, const char* RHS) {
17+
struct stat StatBuf;
18+
if (stat(LHS, &StatBuf)) {
19+
perror("stat failed");
20+
exit(EXIT_FAILURE);
21+
}
22+
ino_t InodeLHS = StatBuf.st_ino;
23+
if (stat(RHS, &StatBuf)) {
24+
perror("stat failed");
25+
exit(EXIT_FAILURE);
26+
}
27+
ino_t InodeRHS = StatBuf.st_ino;
28+
return InodeRHS == InodeLHS;
29+
}
30+
#else
31+
bool isSameDir(const char* LHS, const char* RHS) {
32+
return 0 == strcmp(LHS, RHS);
33+
}
34+
#endif
35+
36+
class OsUtilsTest : public ::testing::Test {
37+
};
38+
39+
TEST_F(OsUtilsTest, getCurrentDSODir) {
40+
std::string DSODir = cl::sycl::detail::OSUtil::getCurrentDSODir();
41+
ASSERT_TRUE(isSameDir(DSODir.c_str(), SYCL_LIB_DIR)) <<
42+
"expected: " << SYCL_LIB_DIR << ", got: " << DSODir;
43+
}

0 commit comments

Comments
 (0)