Skip to content

Commit 2abb131

Browse files
Andrew Savonichevromanovvlad
authored andcommitted
[SYCL] Add OSUtil::getCurrentDSODir() function (#857)
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 e3b76be commit 2abb131

File tree

6 files changed

+206
-1
lines changed

6 files changed

+206
-1
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(
2425
${LIB_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} "${sycl_inc_dir}")

sycl/source/detail/os_util.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
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>
1416
#endif
@@ -20,13 +22,19 @@
2022
#endif // _GNU_SOURCE
2123

2224
#include <cstdio>
25+
#include <cstring>
26+
#include <dlfcn.h>
27+
#include <fstream>
28+
#include <libgen.h> // for dirname
2329
#include <link.h>
30+
#include <linux/limits.h> // for PATH_MAX
2431
#include <sys/sysinfo.h>
2532

2633
#elif defined(SYCL_RT_OS_WINDOWS)
2734

2835
#include <Windows.h>
2936
#include <malloc.h>
37+
#include <shlwapi.h>
3038

3139
#elif defined(SYCL_RT_OS_DARWIN)
3240

@@ -77,6 +85,97 @@ OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
7785
return reinterpret_cast<OSModuleHandle>(Res.Handle);
7886
}
7987

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

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

sycl/unittests/CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@ function(add_sycl_unittest test_dirname)
99
# Enable exception handling for these unit tests
1010
set(LLVM_REQUIRES_EH 1)
1111

12+
if (MSVC AND CMAKE_BUILD_TYPE MATCHES "Debug")
13+
set(sycl_lib "sycld")
14+
else()
15+
set(sycl_lib "sycl")
16+
endif()
17+
1218
add_unittest(SYCLUnitTests ${test_dirname} ${ARGN})
1319
target_link_libraries(${test_dirname}
1420
PRIVATE
15-
sycl
21+
${sycl_lib}
1622
LLVMTestingSupport
1723
OpenCL-Headers
1824
)
@@ -27,3 +33,4 @@ function(add_sycl_unittest test_dirname)
2733
endfunction()
2834

2935
add_subdirectory(pi)
36+
add_subdirectory(misc)

sycl/unittests/misc/CMakeLists.txt

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

sycl/unittests/misc/OsUtils.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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 _WIN32
13+
/// Compare for string equality, but ignore difference between forward slash (/)
14+
/// and backward slash (\).
15+
///
16+
/// This difference can be tricky to avoid, because CMake operates with forward
17+
/// slashes even on Windows, and it can be problematic to convert them when
18+
/// CMake generator expressions are involved. It's easier to handle slashes
19+
/// here in the test itself.
20+
bool isSameDir(const char* LHS, const char* RHS) {
21+
char L = 0, R = 0;
22+
do {
23+
L = *LHS++;
24+
R = *RHS++;
25+
if (L != R) {
26+
if (!((L == '\\' || L == '/') && (R == '\\' || R == '/'))) {
27+
return false;
28+
}
29+
}
30+
} while (L != '\0' && R != '\0');
31+
bool SameLen = (L == '\0' && R == '\0');
32+
return SameLen;
33+
}
34+
#else
35+
#include <sys/stat.h>
36+
#include <stdlib.h>
37+
/// Check with respect to symbolic links
38+
bool isSameDir(const char* LHS, const char* RHS) {
39+
struct stat StatBuf;
40+
if (stat(LHS, &StatBuf)) {
41+
perror("stat failed");
42+
exit(EXIT_FAILURE);
43+
}
44+
ino_t InodeLHS = StatBuf.st_ino;
45+
if (stat(RHS, &StatBuf)) {
46+
perror("stat failed");
47+
exit(EXIT_FAILURE);
48+
}
49+
ino_t InodeRHS = StatBuf.st_ino;
50+
return InodeRHS == InodeLHS;
51+
}
52+
#endif
53+
54+
class OsUtilsTest : public ::testing::Test {
55+
};
56+
57+
TEST_F(OsUtilsTest, getCurrentDSODir) {
58+
std::string DSODir = cl::sycl::detail::OSUtil::getCurrentDSODir();
59+
ASSERT_TRUE(isSameDir(DSODir.c_str(), SYCL_LIB_DIR)) <<
60+
"expected: " << SYCL_LIB_DIR << ", got: " << DSODir;
61+
}

0 commit comments

Comments
 (0)