2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
6 #include "DynamicBackendUtils.hpp"
8 #include <boost/filesystem.hpp>
9 #include <boost/algorithm/string.hpp>
10 #include <boost/log/trivial.hpp>
17 void* DynamicBackendUtils::OpenHandle(const std::string& sharedObjectPath)
19 if (sharedObjectPath.empty())
21 throw RuntimeException("OpenHandle error: shared object path must not be empty");
24 void* sharedObjectHandle = dlopen(sharedObjectPath.c_str(), RTLD_LAZY | RTLD_GLOBAL);
25 if (!sharedObjectHandle)
27 throw RuntimeException(boost::str(boost::format("OpenHandle error: %1%") % GetDlError()));
30 return sharedObjectHandle;
33 void DynamicBackendUtils::CloseHandle(const void* sharedObjectHandle)
35 if (!sharedObjectHandle)
40 dlclose(const_cast<void*>(sharedObjectHandle));
43 bool DynamicBackendUtils::IsBackendCompatible(const BackendVersion &backendVersion)
45 BackendVersion backendApiVersion = IBackendInternal::GetApiVersion();
47 return IsBackendCompatibleImpl(backendApiVersion, backendVersion);
50 bool DynamicBackendUtils::IsBackendCompatibleImpl(const BackendVersion &backendApiVersion,
51 const BackendVersion &backendVersion)
53 return backendVersion.m_Major == backendApiVersion.m_Major &&
54 backendVersion.m_Minor <= backendApiVersion.m_Minor;
57 std::string DynamicBackendUtils::GetDlError()
59 const char* errorMessage = dlerror();
65 return std::string(errorMessage);
68 std::vector<std::string> DynamicBackendUtils::GetBackendPaths(const std::string& overrideBackendPath)
70 // Check if a path where to dynamically load the backends from is given
71 if (!overrideBackendPath.empty())
73 if (!IsPathValid(overrideBackendPath))
75 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given override path for dynamic backends \""
76 << overrideBackendPath << "\" is not valid";
81 return std::vector<std::string>{ overrideBackendPath };
84 // Expects a colon-separated list: DYNAMIC_BACKEND_PATHS="PATH_1:PATH_2:...:PATH_N"
85 const std::string backendPaths = DYNAMIC_BACKEND_PATHS;
87 return GetBackendPathsImpl(backendPaths);
90 std::vector<std::string> DynamicBackendUtils::GetBackendPathsImpl(const std::string& backendPaths)
92 std::unordered_set<std::string> uniqueBackendPaths;
93 std::vector<std::string> tempBackendPaths;
94 std::vector<std::string> validBackendPaths;
96 // Split the given list of paths
97 boost::split(tempBackendPaths, backendPaths, boost::is_any_of(":"));
99 for (const std::string& path : tempBackendPaths)
101 // Check whether the path is valid
102 if (!IsPathValid(path))
107 // Check whether the path is a duplicate
108 auto it = uniqueBackendPaths.find(path);
109 if (it != uniqueBackendPaths.end())
111 // The path is a duplicate
115 // Add the path to the set of unique paths
116 uniqueBackendPaths.insert(path);
118 // Add the path to the list of valid paths
119 validBackendPaths.push_back(path);
122 return validBackendPaths;
125 bool DynamicBackendUtils::IsPathValid(const std::string& path)
129 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path is empty";
133 boost::filesystem::path boostPath(path);
135 if (!boost::filesystem::exists(boostPath))
137 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" does not exist";
141 if (!boost::filesystem::is_directory(boostPath))
143 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" is not a directory";
147 if (!boostPath.is_absolute())
149 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" is not absolute";
156 std::vector<std::string> DynamicBackendUtils::GetSharedObjects(const std::vector<std::string>& backendPaths)
158 std::unordered_set<std::string> uniqueSharedObjects;
159 std::vector<std::string> sharedObjects;
161 for (const std::string& backendPath : backendPaths)
163 using namespace boost::filesystem;
165 // Check if the path is valid. In case of error, IsValidPath will log an error message
166 if (!IsPathValid(backendPath))
171 // Go through all the files in the current backend path
172 for (directory_iterator fileIterator(backendPath); fileIterator != directory_iterator(); fileIterator++)
174 path filePath = *fileIterator;
175 std::string filename = filePath.filename().string();
177 if (filename.empty())
186 // Get the canonical path for the current file, it will throw if for example the file is a
187 // symlink that cannot be resolved
188 canonicalPath = canonical(filePath);
190 catch (const filesystem_error& e)
192 BOOST_LOG_TRIVIAL(warning) << "GetSharedObjects warning: " << e.what();
194 if (canonicalPath.empty())
196 // No such file or perhaps a symlink that couldn't be resolved
200 // Check if the current filename matches the expected naming convention
201 // The expected format is: <vendor>_<name>_backend.so[<version>]
202 // e.g. "Arm_GpuAcc_backend.so" or "Arm_GpuAcc_backend.so.1.2"
203 const std::regex dynamicBackendRegex("^[a-zA-Z0-9]+_[a-zA-Z0-9]+_backend.so(\\.[0-9]+)*$");
205 bool filenameMatch = false;
208 // Match the filename to the expected naming scheme
209 filenameMatch = std::regex_match(filename, dynamicBackendRegex);
211 catch (const std::exception& e)
213 BOOST_LOG_TRIVIAL(warning) << "GetSharedObjects warning: " << e.what();
217 // Filename does not match the expected naming scheme (or an error has occurred)
221 // Append the valid canonical path to the output list only if it's not a duplicate
222 std::string validCanonicalPath = canonicalPath.string();
223 auto it = uniqueSharedObjects.find(validCanonicalPath);
224 if (it == uniqueSharedObjects.end())
226 // Not a duplicate, append the canonical path to the output list
227 sharedObjects.push_back(validCanonicalPath);
229 // Add the canonical path to the collection of unique shared objects
230 uniqueSharedObjects.insert(validCanonicalPath);
235 return sharedObjects;