CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
421 lines
13 KiB
C++
421 lines
13 KiB
C++
// Copyright (c) Advanced Micro Devices, Inc.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#pragma once
|
|
|
|
#include "common/defines.h"
|
|
#include "common/delimit.hpp"
|
|
#include <spdlog/fmt/fmt.h>
|
|
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <dlfcn.h>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <link.h>
|
|
#include <linux/limits.h>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#if !defined(ROCPROFSYS_PATH_LOG_NAME)
|
|
# if defined(ROCPROFSYS_COMMON_LIBRARY_NAME)
|
|
# define ROCPROFSYS_PATH_LOG_NAME "[" ROCPROFSYS_COMMON_LIBRARY_NAME "]"
|
|
# else
|
|
# define ROCPROFSYS_PATH_LOG_NAME
|
|
# endif
|
|
#endif
|
|
|
|
#if !defined(ROCPROFSYS_PATH_LOG_START)
|
|
# if defined(ROCPROFSYS_COMMON_LIBRARY_LOG_START)
|
|
# define ROCPROFSYS_PATH_LOG_START ROCPROFSYS_COMMON_LIBRARY_LOG_START
|
|
# elif defined(TIMEMORY_LOG_COLORS_AVAILABLE)
|
|
# define ROCPROFSYS_PATH_LOG_START \
|
|
fprintf(stderr, "%s", ::tim::log::color::info());
|
|
# else
|
|
# define ROCPROFSYS_PATH_LOG_START
|
|
# endif
|
|
#endif
|
|
|
|
#if !defined(ROCPROFSYS_PATH_LOG_END)
|
|
# if defined(ROCPROFSYS_COMMON_LIBRARY_LOG_END)
|
|
# define ROCPROFSYS_PATH_LOG_END ROCPROFSYS_COMMON_LIBRARY_LOG_END
|
|
# elif defined(TIMEMORY_LOG_COLORS_AVAILABLE)
|
|
# define ROCPROFSYS_PATH_LOG_END fprintf(stderr, "%s", ::tim::log::color::end());
|
|
# else
|
|
# define ROCPROFSYS_PATH_LOG_END
|
|
# endif
|
|
#endif
|
|
|
|
#define ROCPROFSYS_PATH_LOG(CONDITION, ...) \
|
|
if(CONDITION) \
|
|
{ \
|
|
fflush(stderr); \
|
|
ROCPROFSYS_PATH_LOG_START \
|
|
fprintf(stderr, "[rocprof-sys]" ROCPROFSYS_PATH_LOG_NAME "[%i] ", getpid()); \
|
|
fprintf(stderr, __VA_ARGS__); \
|
|
ROCPROFSYS_PATH_LOG_END \
|
|
fflush(stderr); \
|
|
}
|
|
|
|
namespace rocprofsys
|
|
{
|
|
inline namespace common
|
|
{
|
|
namespace path
|
|
{
|
|
inline std::vector<std::string>
|
|
get_link_map(const char*, std::vector<int>&& = { (RTLD_LAZY | RTLD_NOLOAD) },
|
|
bool _include_self = false) ROCPROFSYS_INTERNAL_API;
|
|
|
|
inline auto
|
|
get_link_map(const char* _name, bool&& _include_self,
|
|
std::vector<int>&& _open_modes = {
|
|
(RTLD_LAZY | RTLD_NOLOAD) }) ROCPROFSYS_INTERNAL_API;
|
|
|
|
inline std::string
|
|
get_origin(const std::string&,
|
|
std::vector<int>&& = { (RTLD_LAZY | RTLD_NOLOAD) }) ROCPROFSYS_INTERNAL_API;
|
|
|
|
inline bool
|
|
exists(const std::string& _fname) ROCPROFSYS_INTERNAL_API;
|
|
|
|
inline std::string
|
|
find_path(const std::string& _path, int _verbose,
|
|
const std::string& _search_paths) ROCPROFSYS_INTERNAL_API;
|
|
|
|
[[nodiscard]] inline std::string
|
|
parent_path(std::string_view fpath, std::uint16_t levels = 1) ROCPROFSYS_INTERNAL_API;
|
|
|
|
[[nodiscard]] inline std::string
|
|
realpath(const std::string& path) ROCPROFSYS_INTERNAL_API;
|
|
|
|
inline bool
|
|
is_text_file(const std::string& filename) ROCPROFSYS_INTERNAL_API;
|
|
|
|
[[nodiscard]] inline std::string
|
|
read_symlink(const std::string& path) ROCPROFSYS_INTERNAL_API;
|
|
|
|
[[nodiscard]] inline bool
|
|
is_directory(std::string_view path) ROCPROFSYS_INTERNAL_API;
|
|
|
|
inline std::string
|
|
get_rocprofsys_root() ROCPROFSYS_INTERNAL_API;
|
|
|
|
inline std::string
|
|
get_internal_libpath(const std::string& _lib) ROCPROFSYS_INTERNAL_API;
|
|
|
|
inline std::string
|
|
get_internal_script_path() ROCPROFSYS_INTERNAL_API;
|
|
|
|
inline std::string
|
|
get_internal_libdir() ROCPROFSYS_INTERNAL_API;
|
|
|
|
struct ROCPROFSYS_INTERNAL_API path_type
|
|
{
|
|
enum path_type_e
|
|
{
|
|
directory = 0,
|
|
regular,
|
|
link,
|
|
unknown
|
|
};
|
|
|
|
inline path_type(const std::string&);
|
|
~path_type() = default;
|
|
path_type(const path_type&) = default;
|
|
path_type(path_type&&) = default;
|
|
path_type& operator=(const path_type&) = default;
|
|
path_type& operator=(path_type&&) = default;
|
|
|
|
bool exists() const { return m_type < unknown; }
|
|
explicit operator bool() const { return exists(); }
|
|
|
|
private:
|
|
path_type_e m_type = unknown;
|
|
};
|
|
|
|
//--------------------------------------------------------------------------------------//
|
|
//
|
|
// Implementation
|
|
//
|
|
//--------------------------------------------------------------------------------------//
|
|
|
|
path_type::path_type(const std::string& _fname)
|
|
{
|
|
struct stat _buffer;
|
|
if(lstat(_fname.c_str(), &_buffer) == 0)
|
|
{
|
|
if(S_ISDIR(_buffer.st_mode) != 0)
|
|
m_type = directory;
|
|
else if(S_ISREG(_buffer.st_mode) != 0)
|
|
m_type = regular;
|
|
else if(S_ISLNK(_buffer.st_mode) != 0)
|
|
m_type = link;
|
|
}
|
|
}
|
|
|
|
bool
|
|
exists(const std::string& _fname)
|
|
{
|
|
struct stat _buffer;
|
|
if(lstat(_fname.c_str(), &_buffer) == 0)
|
|
return (S_ISDIR(_buffer.st_mode) != 0 || S_ISREG(_buffer.st_mode) != 0 ||
|
|
S_ISLNK(_buffer.st_mode) != 0);
|
|
return false;
|
|
}
|
|
|
|
std::string
|
|
find_path(const std::string& _path, int _verbose, const std::string& _search_paths)
|
|
{
|
|
if(exists(_path) && !_path.empty() && _path.at(0) == '/') return _path;
|
|
|
|
auto _paths = delimit(_search_paths, ":");
|
|
|
|
constexpr int _verbose_lvl = 2;
|
|
for(const auto& itr : _paths)
|
|
{
|
|
auto _f = fmt::format("{}/{}", itr, _path);
|
|
ROCPROFSYS_PATH_LOG(_verbose >= _verbose_lvl + 1,
|
|
"searching for '%s' in '%s' ...\n", _path.c_str(),
|
|
itr.c_str());
|
|
if(exists(_f))
|
|
{
|
|
ROCPROFSYS_PATH_LOG(_verbose >= _verbose_lvl, "found '%s' in '%s' ...\n",
|
|
_path.c_str(), itr.c_str());
|
|
return _f;
|
|
}
|
|
}
|
|
|
|
for(const auto& itr : _paths)
|
|
{
|
|
if(std::string_view{ ::basename(itr.c_str()) }.find("lib") ==
|
|
std::string_view::npos &&
|
|
!parent_path(itr).empty())
|
|
{
|
|
for(const auto* sitr : { "lib", "lib64", "../lib", "../lib64" })
|
|
{
|
|
auto _f = fmt::format("{}/{}/{}", parent_path(itr), sitr, _path);
|
|
ROCPROFSYS_PATH_LOG(_verbose >= _verbose_lvl + 1,
|
|
"searching for '%s' in '%s' ...\n", _path.c_str(),
|
|
fmt::format("{}/{}", itr, sitr).c_str());
|
|
if(exists(_f))
|
|
{
|
|
ROCPROFSYS_PATH_LOG(_verbose >= _verbose_lvl,
|
|
"found '%s' in '%s' ...\n", _path.c_str(),
|
|
itr.c_str());
|
|
return _f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return _path;
|
|
}
|
|
|
|
/**
|
|
* Get parent directory of @p fpath, walking up @p levels times.
|
|
* Pure lexical operation: no filesystem access and no '.'/'..' resolution.
|
|
* Absolute paths clamp at "/"; relative paths bottom out at "".
|
|
* @code parent_path("/a/b/c", 2) @endcode returns "/a".
|
|
* @param fpath the path to take the parent of
|
|
* @param levels number of components to strip (0 returns @p fpath unchanged)
|
|
* @return the parent path, or "" / "/" at the relative / absolute limit
|
|
*/
|
|
[[nodiscard]] std::string
|
|
parent_path(std::string_view fpath, std::uint16_t levels)
|
|
{
|
|
std::filesystem::path result{ fpath };
|
|
for(std::uint16_t i = 0; i < levels; ++i)
|
|
{
|
|
auto parent = result.parent_path();
|
|
if(parent == result) break; // reached root ("/") or relative bottom ("")
|
|
result = std::move(parent);
|
|
}
|
|
return result.string();
|
|
}
|
|
|
|
/** @brief Read the symbolic link target.
|
|
* @param path The filesystem path to inspect.
|
|
* @return The link target as a string, or @p path unchanged if @p path is not
|
|
* a symbolic link or if any filesystem error occurs.
|
|
*/
|
|
[[nodiscard]] std::string
|
|
read_symlink(const std::string& path)
|
|
{
|
|
std::error_code error;
|
|
auto target = std::filesystem::read_symlink(path, error);
|
|
return (error) ? path : target.string();
|
|
}
|
|
|
|
/**
|
|
* @brief Check whether a path exists and is a directory.
|
|
*
|
|
* Follows symbolic links: a symlink that points at a directory returns true.
|
|
* Never throws; any filesystem error (missing path, broken symlink,
|
|
* permission failure) yields false.
|
|
*
|
|
* @param path Filesystem path to test.
|
|
* @return true if @p path resolves to a directory, false otherwise.
|
|
*/
|
|
[[nodiscard]] bool
|
|
is_directory(std::string_view path)
|
|
{
|
|
std::error_code unused_error_code;
|
|
return std::filesystem::is_directory(path, unused_error_code);
|
|
}
|
|
|
|
/**
|
|
* Resolve @p path to its canonical absolute form.
|
|
* Filesystem operation: follows symlinks and collapses '.'/'..'; the path
|
|
* must exist. On any error (missing path, permission denied) @p path is
|
|
* returned unchanged.
|
|
* @code realpath("/a/./b/../c") @endcode returns "/a/c" (when it exists).
|
|
* @param path the path to canonicalize
|
|
* @return the canonical absolute path, or @p path unchanged on error
|
|
*/
|
|
[[nodiscard]] std::string
|
|
realpath(const std::string& path)
|
|
{
|
|
std::error_code error;
|
|
auto canon = std::filesystem::canonical(path, error);
|
|
return (error) ? path : canon.string();
|
|
}
|
|
|
|
bool
|
|
is_text_file(const std::string& filename)
|
|
{
|
|
std::ifstream _file{ filename, std::ios::in | std::ios::binary };
|
|
if(!_file.is_open())
|
|
{
|
|
ROCPROFSYS_PATH_LOG(0, "Error! '%s' could not be opened...\n", filename.c_str());
|
|
return false;
|
|
}
|
|
|
|
constexpr size_t buffer_size = 1024;
|
|
char buffer[buffer_size];
|
|
while(_file.read(buffer, sizeof(buffer)))
|
|
{
|
|
for(char itr : buffer)
|
|
{
|
|
if(itr == '\0') return false;
|
|
}
|
|
}
|
|
|
|
if(_file.gcount() > 0)
|
|
{
|
|
for(std::streamsize i = 0; i < _file.gcount(); ++i)
|
|
{
|
|
if(buffer[i] == '\0') return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::vector<std::string>
|
|
get_link_map(const char* _name, std::vector<int>&& _open_modes, bool _include_self)
|
|
{
|
|
void* _handle = nullptr;
|
|
bool _noload = false;
|
|
for(auto _mode : _open_modes)
|
|
{
|
|
_handle = dlopen(_name, _mode);
|
|
_noload = (_mode & RTLD_NOLOAD) == RTLD_NOLOAD;
|
|
if(_handle) break;
|
|
}
|
|
|
|
auto _chain = std::vector<std::string>{};
|
|
if(_handle)
|
|
{
|
|
struct link_map* _link_map = nullptr;
|
|
dlinfo(_handle, RTLD_DI_LINKMAP, &_link_map);
|
|
// if include_self is false, start at next library
|
|
struct link_map* _next = (_include_self) ? _link_map : _link_map->l_next;
|
|
while(_next)
|
|
{
|
|
if(_next->l_name != nullptr && !std::string_view{ _next->l_name }.empty())
|
|
{
|
|
_chain.emplace_back(_next->l_name);
|
|
}
|
|
_next = _next->l_next;
|
|
}
|
|
|
|
if(_noload == false) dlclose(_handle);
|
|
}
|
|
return _chain;
|
|
}
|
|
|
|
auto
|
|
get_link_map(const char* _name, bool&& _include_self, std::vector<int>&& _open_modes)
|
|
{
|
|
return get_link_map(_name, std::move(_open_modes), _include_self);
|
|
}
|
|
|
|
std::string
|
|
get_origin(const std::string& _filename, std::vector<int>&& _open_modes)
|
|
{
|
|
void* _handle = nullptr;
|
|
bool _noload = false;
|
|
for(auto _mode : _open_modes)
|
|
{
|
|
_handle = dlopen(_filename.c_str(), _mode);
|
|
_noload = (_mode & RTLD_NOLOAD) == RTLD_NOLOAD;
|
|
if(_handle) break;
|
|
}
|
|
|
|
auto _chain = std::vector<std::string>{};
|
|
if(_handle)
|
|
{
|
|
char _buffer[PATH_MAX];
|
|
memset(_buffer, '\0', PATH_MAX * sizeof(char));
|
|
if(dlinfo(_handle, RTLD_DI_ORIGIN, &_buffer) == 0)
|
|
{
|
|
auto _origin = std::string{ _buffer };
|
|
if(exists(_origin)) return _origin;
|
|
}
|
|
|
|
if(_noload == false) dlclose(_handle);
|
|
}
|
|
|
|
return std::string{};
|
|
}
|
|
|
|
std::string
|
|
get_rocprofsys_root()
|
|
{
|
|
// strip 2 levels from exe filepath to reach root
|
|
return parent_path(realpath("/proc/self/exe"), 2);
|
|
}
|
|
|
|
std::string
|
|
get_internal_libpath(const std::string& _lib)
|
|
{
|
|
auto _root = get_rocprofsys_root();
|
|
for(const auto* libdir : { "lib", "lib64" })
|
|
{
|
|
auto _candidate = fmt::format("{}/{}/{}", _root, libdir, _lib);
|
|
if(exists(_candidate)) return _candidate;
|
|
}
|
|
return fmt::format("{}/lib/{}", _root, _lib);
|
|
}
|
|
|
|
std::string
|
|
get_internal_script_path()
|
|
{
|
|
auto _root = get_rocprofsys_root();
|
|
return _root + "/libexec/rocprofiler-systems";
|
|
}
|
|
|
|
std::string
|
|
get_internal_libdir()
|
|
{
|
|
return get_rocprofsys_root() + "/lib";
|
|
}
|
|
|
|
} // namespace path
|
|
} // namespace common
|
|
} // namespace rocprofsys
|