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 1 commit
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
122 changes: 122 additions & 0 deletions sycl/source/detail/os_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@
#include <CL/sycl/detail/os_util.hpp>
#include <CL/sycl/exception.hpp>

#include <cassert>

#ifdef SYCL_RT_OS_POSIX_SUPPORT
#include <cstdlib>
#include <cstring>
#include <fstream>
#endif

#if defined(SYCL_RT_OS_LINUX)
Expand All @@ -20,13 +24,19 @@
#endif // _GNU_SOURCE

#include <cstdio>
#include <dlfcn.h>
#include <libgen.h> // for dirname
#include <link.h>
#include <link.h>
#include <linux/limits.h> // for PATH_MAX
#include <stdio.h>
#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 +87,99 @@ OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
return reinterpret_cast<OSModuleHandle>(Res.Handle);
}

bool procMapsAddressInRange(std::istream &Stream, uintptr_t Addr) {
uintptr_t Start, End;
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 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:
uintptr_t Offset, DevMajor, DevMinor, Inode;
Stream >> Offset;
assert(Stream.peek() == ' ');
Stream.ignore(1);

Stream >> DevMajor;
assert(Stream.peek() == ':' &&
"Couldn't read dev field in /proc/self/maps");
Stream.ignore(1);
Stream >> DevMinor;
assert(Stream.peek() == ' ');
Stream.ignore(1);

Stream >> Inode;
assert(Stream.peek() == ' ');
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
return std::string(
dirname(const_cast<char*>(Tmp.c_str())));
}

#elif defined(SYCL_RT_OS_WINDOWS)
OSModuleHandle OSUtil::getOSModuleHandle(const void *VirtAddr) {
HMODULE PhModule;
Expand All @@ -93,6 +196,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
1 change: 1 addition & 0 deletions sycl/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ function(add_sycl_unittest test_dirname)
endfunction()

add_subdirectory(pi)
add_subdirectory(misc)
7 changes: 7 additions & 0 deletions sycl/unittests/misc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
set(CMAKE_CXX_EXTENSIONS OFF)

set(sycl_lib_dir $<TARGET_FILE_DIR:sycl>)
add_definitions(-DSYCL_LIB_DIR="${sycl_lib_dir}")
add_sycl_unittest(MiscTests
OsUtils.cpp
)
43 changes: 43 additions & 0 deletions sycl/unittests/misc/OsUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//==---- 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 __unix__
#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;
}
#else
bool isSameDir(const char* LHS, const char* RHS) {
return 0 == strcmp(LHS, RHS);
}
#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;
}