Skip to content
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
8 changes: 8 additions & 0 deletions scripts/core/INTRO.rst
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,14 @@ Specific environment variables can be set to control the behavior of unified run

See the Layers_ section for details of the layers currently included in the runtime.

.. envvar:: UR_LOADER_PRELOAD_FILTER

If set, the loader will read `ONEAPI_DEVICE_SELECTOR` before loading the UR Adapters to determine which backends should be loaded.

.. note::

This environment variable is default enabled on Linux, but default disabled on Windows.

Service identifiers
---------------------

Expand Down
8 changes: 5 additions & 3 deletions source/adapters/level_zero/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -769,9 +769,11 @@ ur_result_t ur_context_handle_t_::getAvailableCommandList(
CommandList =
Queue->CommandListMap
.emplace(ZeCommandList,
ur_command_list_info_t(ZeFence, true, false,
ZeCommandQueue, ZeQueueDesc,
Queue->useCompletionBatching()))
ur_command_list_info_t(
ZeFence, true, false, ZeCommandQueue, ZeQueueDesc,
Queue->useCompletionBatching(), true,
ZeCommandListIt->second.InOrderList,
ZeCommandListIt->second.IsImmediate))
.first;
}
ZeCommandListCache.erase(ZeCommandListIt);
Expand Down
20 changes: 20 additions & 0 deletions source/loader/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,26 @@ if(UR_ENABLE_SANITIZER)
)
target_include_directories(ur_loader PRIVATE ${LLVM_INCLUDE_DIRS})
target_link_libraries(ur_loader PRIVATE LLVMSupport LLVMSymbolize)
# In in-tree build, if LLVM is built with libc++, we also need to build
# symbolizer.cpp with libc++ abi and link libc++ in.
if(NOT UR_STANDALONE_BUILD AND LLVM_LIBCXX_USED)
execute_process(
COMMAND ${CMAKE_CXX_COMPILER} --print-file-name=libc++.a
OUTPUT_VARIABLE LIBCXX_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND ${CMAKE_CXX_COMPILER} --print-file-name=libc++abi.a
OUTPUT_VARIABLE LIBCXX_ABI_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE)
set_property(SOURCE
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/linux/symbolizer.cpp
APPEND_STRING PROPERTY COMPILE_FLAGS
" -stdlib=libc++ ")
if(NOT EXISTS ${LIBCXX_PATH} OR NOT EXISTS ${LIBCXX_ABI_PATH})
message(FATAL_ERROR "libc++ is required but can't find the libraries")
endif()
target_link_libraries(ur_loader PRIVATE ${LIBCXX_PATH} ${LIBCXX_ABI_PATH})
endif()
endif()

target_include_directories(ur_loader PRIVATE
Expand Down
15 changes: 11 additions & 4 deletions source/loader/layers/sanitizer/linux/symbolizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ llvm::symbolize::PrinterConfig GetPrinterConfig() {

extern "C" {

bool SymbolizeCode(const std::string ModuleName, uint64_t ModuleOffset,
std::string &Result) {
void SymbolizeCode(const char *ModuleName, uint64_t ModuleOffset,
char *ResultString, size_t ResultSize, size_t *RetSize) {
std::string Result;
llvm::raw_string_ostream OS(Result);
llvm::symbolize::Request Request{ModuleName, ModuleOffset};
llvm::symbolize::PrinterConfig Config =
Expand All @@ -51,10 +52,16 @@ bool SymbolizeCode(const std::string ModuleName, uint64_t ModuleOffset,
{ModuleOffset, llvm::object::SectionedAddress::UndefSection});

if (!ResOrErr) {
return false;
return;
}
Printer->print(Request, *ResOrErr);
ur_sanitizer_layer::GetSymbolizer()->pruneCache();
return true;
if (RetSize) {
*RetSize = Result.size() + 1;
}
if (ResultString) {
std::strncpy(ResultString, Result.c_str(), ResultSize);
ResultString[ResultSize - 1] = '\0';
}
}
}
21 changes: 14 additions & 7 deletions source/loader/layers/sanitizer/stacktrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@

extern "C" {

__attribute__((weak)) bool SymbolizeCode(const std::string ModuleName,
__attribute__((weak)) void SymbolizeCode(const char *ModuleName,
uint64_t ModuleOffset,
std::string &Result);
char *ResultString, size_t ResultSize,
size_t *RetSize);
}

namespace ur_sanitizer_layer {
Expand Down Expand Up @@ -49,7 +50,7 @@ void ParseBacktraceInfo(BacktraceInfo BI, std::string &ModuleName,
// Parse symbolizer output in the following formats:
// <function_name>
// <file_name>:<line_number>[:<column_number>]
SourceInfo ParseSymbolizerOutput(std::string Output) {
SourceInfo ParseSymbolizerOutput(const std::string &Output) {
SourceInfo Info;
// Parse function name
size_t End = Output.find_first_of('\n');
Expand Down Expand Up @@ -98,8 +99,14 @@ void StackTrace::print() const {
std::string ModuleName;
uptr Offset;
ParseBacktraceInfo(BI, ModuleName, Offset);
if (SymbolizeCode(ModuleName, Offset, Result)) {
SourceInfo SrcInfo = ParseSymbolizerOutput(std::move(Result));
size_t ResultSize = 0;
SymbolizeCode(ModuleName.c_str(), Offset, nullptr, 0, &ResultSize);
if (ResultSize) {
std::vector<char> ResultVector(ResultSize);
SymbolizeCode(ModuleName.c_str(), Offset, ResultVector.data(),
ResultSize, nullptr);
std::string Result((char *)ResultVector.data());
SourceInfo SrcInfo = ParseSymbolizerOutput(Result);
if (SrcInfo.file != "??") {
getContext()->logger.always(" #{} in {} {}:{}:{}", index,
SrcInfo.function, SrcInfo.file,
Expand All @@ -109,10 +116,10 @@ void StackTrace::print() const {
SrcInfo.function, ModuleName,
(void *)Offset);
}
continue;
}
} else {
getContext()->logger.always(" #{} {}", index, BI);
}
getContext()->logger.always(" #{} {}", index, BI);
++index;
}
getContext()->logger.always("");
Expand Down
113 changes: 113 additions & 0 deletions source/loader/ur_adapter_registry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,123 @@ class AdapterRegistry {
return paths.empty() ? std::nullopt : std::optional(paths);
}

ur_result_t readPreFilterODS(std::string platformBackendName) {
// TODO: Refactor this to the common code such that both the prefilter and urDeviceGetSelected use the same functionality.
bool acceptLibrary = true;
std::optional<EnvVarMap> odsEnvMap;
try {
odsEnvMap = getenv_to_map("ONEAPI_DEVICE_SELECTOR", false);

} catch (...) {
// If the selector is malformed, then we ignore selector and return success.
logger::error("ERROR: missing backend, format of filter = "
"'[!]backend:filterStrings'");
return UR_RESULT_SUCCESS;
}
logger::debug(
"getenv_to_map parsed env var and {} a map",
(odsEnvMap.has_value() ? "produced" : "failed to produce"));

// if the ODS env var is not set at all, then pretend it was set to the default
using EnvVarMap = std::map<std::string, std::vector<std::string>>;
EnvVarMap mapODS =
odsEnvMap.has_value() ? odsEnvMap.value() : EnvVarMap{{"*", {"*"}}};
for (auto &termPair : mapODS) {
std::string backend = termPair.first;
// TODO: Figure out how to process all ODS errors rather than returning
// on the first error.
if (backend.empty()) {
// FIXME: never true because getenv_to_map rejects this case
// malformed term: missing backend -- output ERROR, then continue
logger::error("ERROR: missing backend, format of filter = "
"'[!]backend:filterStrings'");
continue;
}
logger::debug("ONEAPI_DEVICE_SELECTOR Pre-Filter with backend '{}' "
"and platform library name '{}'",
backend, platformBackendName);
enum FilterType {
AcceptFilter,
DiscardFilter,
} termType =
(backend.front() != '!') ? AcceptFilter : DiscardFilter;
logger::debug(
"termType is {}",
(termType != AcceptFilter ? "DiscardFilter" : "AcceptFilter"));
if (termType != AcceptFilter) {
logger::debug("DEBUG: backend was '{}'", backend);
backend.erase(backend.cbegin());
logger::debug("DEBUG: backend now '{}'", backend);
}

// Verify that the backend string is valid, otherwise ignore the backend.
if ((strcmp(backend.c_str(), "*") != 0) &&
(strcmp(backend.c_str(), "level_zero") != 0) &&
(strcmp(backend.c_str(), "opencl") != 0) &&
(strcmp(backend.c_str(), "cuda") != 0) &&
(strcmp(backend.c_str(), "hip") != 0)) {
logger::debug("ONEAPI_DEVICE_SELECTOR Pre-Filter with illegal "
"backend '{}' ",
backend);
continue;
}

// case-insensitive comparison by converting both tolower
std::transform(platformBackendName.begin(),
platformBackendName.end(),
platformBackendName.begin(),
[](unsigned char c) { return std::tolower(c); });
std::transform(backend.begin(), backend.end(), backend.begin(),
[](unsigned char c) { return std::tolower(c); });
std::size_t nameFound = platformBackendName.find(backend);

bool backendFound = nameFound != std::string::npos;
if (termType == AcceptFilter) {
if (backend.front() != '*' && !backendFound) {
logger::debug(
"The ONEAPI_DEVICE_SELECTOR backend name '{}' was not "
"found in the platform library name '{}'",
backend, platformBackendName);
acceptLibrary = false;
continue;
} else if (backend.front() == '*' || backendFound) {
return UR_RESULT_SUCCESS;
}
} else {
if (backendFound || backend.front() == '*') {
acceptLibrary = false;
logger::debug(
"The ONEAPI_DEVICE_SELECTOR backend name for discard "
"'{}' was found in the platform library name '{}'",
backend, platformBackendName);
continue;
}
}
}
if (acceptLibrary) {
return UR_RESULT_SUCCESS;
}
return UR_RESULT_ERROR_INVALID_VALUE;
}

void discoverKnownAdapters() {
auto searchPathsEnvOpt = getEnvAdapterSearchPaths();
auto loaderLibPathOpt = getLoaderLibPath();
#if defined(_WIN32)
bool loaderPreFilter = getenv_tobool("UR_LOADER_PRELOAD_FILTER", false);
#else
bool loaderPreFilter = getenv_tobool("UR_LOADER_PRELOAD_FILTER", true);
#endif
for (const auto &adapterName : knownAdapterNames) {

if (loaderPreFilter) {
if (readPreFilterODS(adapterName) != UR_RESULT_SUCCESS) {
logger::debug("The adapter '{}' was removed based on the "
"pre-filter from ONEAPI_DEVICE_SELECTOR.",
adapterName);
continue;
}
}
std::vector<fs::path> loadPaths;

// Adapter search order:
Expand Down
4 changes: 4 additions & 0 deletions test/loader/adapter_registry/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,7 @@ add_adapter_reg_search_test(search-order
SEARCH_PATH ${TEST_SEARCH_PATH}
ENVS "TEST_ADAPTER_SEARCH_PATH=\"${TEST_SEARCH_PATH}\"" "TEST_CUR_SEARCH_PATH=\"${TEST_BIN_PATH}\""
SOURCES search_order.cpp)

add_adapter_reg_search_test(prefilter
SEARCH_PATH ""
SOURCES prefilter.cpp)
44 changes: 44 additions & 0 deletions test/loader/adapter_registry/fixtures.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,49 @@ struct adapterRegSearchTest : ::testing::Test {
}
}
};
#ifndef _WIN32
struct adapterPreFilterTest : ::testing::Test {
ur_loader::AdapterRegistry *registry;
const fs::path levelzeroLibName =
MAKE_LIBRARY_NAME("ur_adapter_level_zero", "0");
std::function<bool(const fs::path &)> islevelzeroLibName =
[this](const fs::path &path) { return path == levelzeroLibName; };

std::function<bool(const std::vector<fs::path> &)> haslevelzeroLibName =
[this](const std::vector<fs::path> &paths) {
return std::any_of(paths.cbegin(), paths.cend(),
islevelzeroLibName);
};

const fs::path openclLibName = MAKE_LIBRARY_NAME("ur_adapter_opencl", "0");
std::function<bool(const fs::path &)> isOpenclLibName =
[this](const fs::path &path) { return path == openclLibName; };

std::function<bool(const std::vector<fs::path> &)> hasOpenclLibName =
[this](const std::vector<fs::path> &paths) {
return std::any_of(paths.cbegin(), paths.cend(), isOpenclLibName);
};

const fs::path cudaLibName = MAKE_LIBRARY_NAME("ur_adapter_cuda", "0");
std::function<bool(const fs::path &)> isCudaLibName =
[this](const fs::path &path) { return path == cudaLibName; };

std::function<bool(const std::vector<fs::path> &)> hasCudaLibName =
[this](const std::vector<fs::path> &paths) {
return std::any_of(paths.cbegin(), paths.cend(), isCudaLibName);
};

void SetUp(std::string filter) {
try {
setenv("ONEAPI_DEVICE_SELECTOR", filter.c_str(), 1);
registry = new ur_loader::AdapterRegistry;
} catch (const std::invalid_argument &e) {
FAIL() << e.what();
}
}
void SetUp() override {}
void TearDown() override { delete registry; }
};
#endif

#endif // UR_ADAPTER_REG_TEST_HELPERS_H
Loading