Skip to content

[SYCL] Add OSUtil::getCurrentDSODir() function #857

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions sycl/include/CL/sycl/detail/os_util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <cstdint>
#include <stdlib.h>
#include <string>

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

/// Returns an absolute path to a directory where the object was found.
static std::string getCurrentDSODir();

/// Returns a directory component of a path.
static std::string getDirName(const char* Path);

/// Module handle for the executable module - it is assumed there is always
/// single one at most.
static constexpr OSModuleHandle ExeModuleHandle = -1;

#ifdef SYCL_RT_OS_WINDOWS
static constexpr const char* DirSep = "\\";
#else
static constexpr const char* DirSep = "/";
#endif

/// Returns the amount of RAM available for the operating system.
static size_t getOSMemSize();

Expand Down
1 change: 1 addition & 0 deletions sycl/source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function(add_sycl_rt_library LIB_NAME)

if (MSVC)
target_compile_definitions(${LIB_NAME} PRIVATE __SYCL_BUILD_SYCL_DLL )
target_link_libraries(${LIB_NAME} PRIVATE shlwapi)
endif()
target_include_directories(${LIB_NAME} PRIVATE "${sycl_inc_dir}")
target_link_libraries(${LIB_NAME}
Expand Down
118 changes: 118 additions & 0 deletions sycl/source/detail/os_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include <CL/sycl/detail/os_util.hpp>
#include <CL/sycl/exception.hpp>

#include <cassert>

#ifdef SYCL_RT_OS_POSIX_SUPPORT
#include <cstdlib>
#endif
Expand All @@ -20,13 +22,19 @@
#endif // _GNU_SOURCE

#include <cstdio>
#include <cstring>
#include <dlfcn.h>
#include <fstream>
#include <libgen.h> // for dirname
#include <link.h>
#include <linux/limits.h> // for PATH_MAX
#include <sys/sysinfo.h>

#elif defined(SYCL_RT_OS_WINDOWS)

#include <Windows.h>
#include <malloc.h>
#include <shlwapi.h>

#elif defined(SYCL_RT_OS_DARWIN)

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

bool procMapsAddressInRange(std::istream &Stream, uintptr_t Addr) {
uintptr_t Start = 0, End = 0;
Stream >> Start;
assert(!Stream.fail() && Stream.peek() == '-' &&
"Couldn't read /proc/self/maps correctly");
Stream.ignore(1);

Stream >> End;
assert(!Stream.fail() && Stream.peek() == ' ' &&
"Couldn't read /proc/self/maps correctly");
Stream.ignore(1);

return Addr >= Start && Addr < End;
}

/// Returns an absolute path to a directory where the object was found.
std::string OSUtil::getCurrentDSODir() {
// Examine /proc/self/maps and find where this function (getCurrendDSODir)
// comes from - this is supposed to be an absolute path to libsycl.so.
//
// File structure is the following:
// address perms offset dev inode pathname
// 00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/foo
// 007c2000-007c8000 r--p 001c2000 fc:05 52567930 /usr/bin/bar
//
// We need to:
//
// 1) Iterate over lines and find the line which have an address of the
// current function in an `address' range.
//
// 2) Check that perms have read and executable flags (since we do execute
// this function).
//
// 3) Skip offset, dev, inode
//
// 4) Extract an absolute path to a filename and get a dirname from it.
//
uintptr_t CurrentFunc = (uintptr_t) &getCurrentDSODir;
std::ifstream Stream("/proc/self/maps");
Stream >> std::hex;
while (!Stream.eof()) {
if (!procMapsAddressInRange(Stream, CurrentFunc)) {
// Skip the rest until an EOL and check the next line
Stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
continue;
}

char Perm[4];
Stream.readsome(Perm, sizeof(Perm));
assert(Perm[0] == 'r' && Perm[2] == 'x' &&
"Invalid flags in /proc/self/maps");
assert(Stream.peek() == ' ');
Stream.ignore(1);

// Read and ignore the following:
// offset
Stream.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
Stream.ignore(1);
// dev major
Stream.ignore(std::numeric_limits<std::streamsize>::max(), ':');
Stream.ignore(1);
// dev minor
Stream.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
Stream.ignore(1);
// inode
Stream.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
Stream.ignore(1);

// Now read the path: it is padded with whitespaces, so we skip them
// first.
while (Stream.peek() == ' ') {
Stream.ignore(1);
}
char Path[PATH_MAX];
Stream.getline(Path, PATH_MAX - 1);
Path[PATH_MAX - 1] = '\0';
return OSUtil::getDirName(Path);
}
assert(false && "Unable to find the current function in /proc/self/maps");
return "";
}

std::string OSUtil::getDirName(const char* Path) {
std::string Tmp(Path);
// dirname(3) needs a writable C string: a null-terminator is written where a
// path should split.
size_t TruncatedSize = strlen(dirname(const_cast<char*>(Tmp.c_str())));
Tmp.resize(TruncatedSize);
return Tmp;
}

#elif defined(SYCL_RT_OS_WINDOWS)
OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
HMODULE PhModule;
Expand All @@ -93,6 +192,25 @@ OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
return reinterpret_cast<OSModuleHandle>(PhModule);
}

/// Returns an absolute path where the object was found.
std::string OSUtil::getCurrentDSODir() {
char Path[MAX_PATH];
Path[0] = '\0';
Path[sizeof(Path) - 1] = '\0';
DWORD Ret = GetModuleFileNameA(
reinterpret_cast<HMODULE>(getOSModuleHandle(&getCurrentDSODir)),
reinterpret_cast<LPSTR>(&Path),
sizeof(Path));
assert(Ret < sizeof(Path) && "Path is longer than PATH_MAX?");
assert(Ret > 0 && "GetModuleFileNameA failed");

BOOL RetCode = PathRemoveFileSpecA(reinterpret_cast<LPSTR>(&Path));
assert(RetCode && "PathRemoveFileSpecA failed");
(void)RetCode;

return Path;
};

#elif defined(SYCL_RT_OS_DARWIN)
OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
Dl_info Res;
Expand Down
9 changes: 8 additions & 1 deletion sycl/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ function(add_sycl_unittest test_dirname)
# Enable exception handling for these unit tests
set(LLVM_REQUIRES_EH 1)

if (MSVC AND CMAKE_BUILD_TYPE MATCHES "Debug")
set(sycl_lib "sycld")
else()
set(sycl_lib "sycl")
endif()

add_unittest(SYCLUnitTests ${test_dirname} ${ARGN})
target_link_libraries(${test_dirname}
PRIVATE
sycl
${sycl_lib}
LLVMTestingSupport
OpenCL-Headers
)
Expand All @@ -27,3 +33,4 @@ function(add_sycl_unittest test_dirname)
endfunction()

add_subdirectory(pi)
add_subdirectory(misc)
5 changes: 5 additions & 0 deletions sycl/unittests/misc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
set(sycl_lib_dir $<TARGET_FILE_DIR:sycl>)
add_definitions(-DSYCL_LIB_DIR="${sycl_lib_dir}")
add_sycl_unittest(MiscTests
OsUtils.cpp
)
61 changes: 61 additions & 0 deletions sycl/unittests/misc/OsUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//==---- OsUtils.cpp --- os_utils unit test --------------------------------==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include <CL/sycl/detail/os_util.hpp>
#include <gtest/gtest.h>

#ifdef _WIN32
/// Compare for string equality, but ignore difference between forward slash (/)
/// and backward slash (\).
///
/// This difference can be tricky to avoid, because CMake operates with forward
/// slashes even on Windows, and it can be problematic to convert them when
/// CMake generator expressions are involved. It's easier to handle slashes
/// here in the test itself.
bool isSameDir(const char* LHS, const char* RHS) {
char L = 0, R = 0;
do {
L = *LHS++;
R = *RHS++;
if (L != R) {
if (!((L == '\\' || L == '/') && (R == '\\' || R == '/'))) {
return false;
}
}
} while (L != '\0' && R != '\0');
bool SameLen = (L == '\0' && R == '\0');
return SameLen;
}
#else
#include <sys/stat.h>
#include <stdlib.h>
/// Check with respect to symbolic links
bool isSameDir(const char* LHS, const char* RHS) {
struct stat StatBuf;
if (stat(LHS, &StatBuf)) {
perror("stat failed");
exit(EXIT_FAILURE);
}
ino_t InodeLHS = StatBuf.st_ino;
if (stat(RHS, &StatBuf)) {
perror("stat failed");
exit(EXIT_FAILURE);
}
ino_t InodeRHS = StatBuf.st_ino;
return InodeRHS == InodeLHS;
}
#endif

class OsUtilsTest : public ::testing::Test {
};

TEST_F(OsUtilsTest, getCurrentDSODir) {
std::string DSODir = cl::sycl::detail::OSUtil::getCurrentDSODir();
ASSERT_TRUE(isSameDir(DSODir.c_str(), SYCL_LIB_DIR)) <<
"expected: " << SYCL_LIB_DIR << ", got: " << DSODir;
}