blob: 38ac5ad6af5e9fbe96e5bebec2fd9902e6ea14da [file] [log] [blame]
Matteo Martincighd73cecb2019-07-24 09:15:00 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "DynamicBackendUtils.hpp"
7
Jan Eilers4a539fc2019-07-25 17:08:37 +01008#include <boost/filesystem.hpp>
Matteo Martincighe7d44982019-08-05 12:16:47 +01009#include <boost/algorithm/string.hpp>
10#include <boost/log/trivial.hpp>
11
Jan Eilers4a539fc2019-07-25 17:08:37 +010012#include <regex>
13
Matteo Martincighd73cecb2019-07-24 09:15:00 +010014namespace armnn
15{
16
17void* DynamicBackendUtils::OpenHandle(const std::string& sharedObjectPath)
18{
19 if (sharedObjectPath.empty())
20 {
21 throw RuntimeException("OpenHandle error: shared object path must not be empty");
22 }
23
Matteo Martincighe54aa062019-08-05 14:12:11 +010024 void* sharedObjectHandle = dlopen(sharedObjectPath.c_str(), RTLD_LAZY);
Matteo Martincighd73cecb2019-07-24 09:15:00 +010025 if (!sharedObjectHandle)
26 {
Matteo Martincighb19d2e92019-07-25 14:04:40 +010027 throw RuntimeException(boost::str(boost::format("OpenHandle error: %1%") % GetDlError()));
Matteo Martincighd73cecb2019-07-24 09:15:00 +010028 }
29
30 return sharedObjectHandle;
31}
32
33void DynamicBackendUtils::CloseHandle(const void* sharedObjectHandle)
34{
35 if (!sharedObjectHandle)
36 {
37 return;
38 }
39
40 dlclose(const_cast<void*>(sharedObjectHandle));
41}
42
Matteo Martincighac60d282019-07-25 15:25:44 +010043bool DynamicBackendUtils::IsBackendCompatible(const BackendVersion &backendVersion)
44{
45 BackendVersion backendApiVersion = IBackendInternal::GetApiVersion();
46
47 return IsBackendCompatibleImpl(backendApiVersion, backendVersion);
48}
49
50bool DynamicBackendUtils::IsBackendCompatibleImpl(const BackendVersion &backendApiVersion,
51 const BackendVersion &backendVersion)
52{
53 return backendVersion.m_Major == backendApiVersion.m_Major &&
54 backendVersion.m_Minor <= backendApiVersion.m_Minor;
55}
56
Matteo Martincighd73cecb2019-07-24 09:15:00 +010057std::string DynamicBackendUtils::GetDlError()
58{
59 const char* errorMessage = dlerror();
60 if (!errorMessage)
61 {
62 return "";
63 }
64
65 return std::string(errorMessage);
66}
67
Matteo Martincighe7d44982019-08-05 12:16:47 +010068std::vector<std::string> DynamicBackendUtils::GetBackendPaths(const std::string& overrideBackendPath)
69{
70 // Check if a path where to dynamically load the backends from is given
71 if (!overrideBackendPath.empty())
72 {
73 if (!IsPathValid(overrideBackendPath))
74 {
75 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given override path for dynamic backends \""
76 << overrideBackendPath << "\" is not valid";
77
78 return {};
79 }
80
81 return std::vector<std::string>{ overrideBackendPath };
82 }
83
84 // Expects a colon-separated list: DYNAMIC_BACKEND_PATHS="PATH_1:PATH_2:...:PATH_N"
85 const std::string backendPaths = DYNAMIC_BACKEND_PATHS;
86
87 return GetBackendPathsImpl(backendPaths);
88}
89
90std::vector<std::string> DynamicBackendUtils::GetBackendPathsImpl(const std::string& backendPaths)
91{
92 std::unordered_set<std::string> uniqueBackendPaths;
93 std::vector<std::string> tempBackendPaths;
94 std::vector<std::string> validBackendPaths;
95
96 // Split the given list of paths
97 boost::split(tempBackendPaths, backendPaths, boost::is_any_of(":"));
98
99 for (const std::string& path : tempBackendPaths)
100 {
101 // Check whether the path is valid
102 if (!IsPathValid(path))
103 {
104 continue;
105 }
106
107 // Check whether the path is a duplicate
108 auto it = uniqueBackendPaths.find(path);
109 if (it != uniqueBackendPaths.end())
110 {
111 // The path is a duplicate
112 continue;
113 }
114
115 // Add the path to the set of unique paths
116 uniqueBackendPaths.insert(path);
117
118 // Add the path to the list of valid paths
119 validBackendPaths.push_back(path);
120 }
121
122 return validBackendPaths;
123}
124
125bool DynamicBackendUtils::IsPathValid(const std::string& path)
126{
127 if (path.empty())
128 {
129 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path is empty";
130 return false;
131 }
132
133 boost::filesystem::path boostPath(path);
134
135 if (!boost::filesystem::exists(boostPath))
136 {
137 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" does not exist";
138 return false;
139 }
140
141 if (!boost::filesystem::is_directory(boostPath))
142 {
143 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" is not a directory";
144 return false;
145 }
146
147 if (!boostPath.is_absolute())
148 {
149 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" is not absolute";
150 return false;
151 }
152
153 return true;
154}
155
Jan Eilers4a539fc2019-07-25 17:08:37 +0100156std::vector<std::string> DynamicBackendUtils::GetSharedObjects(const std::vector<std::string>& backendPaths)
157{
158 std::unordered_set<std::string> uniqueSharedObjects;
159 std::vector<std::string> sharedObjects;
160
161 for (const std::string& backendPath : backendPaths)
162 {
163 using namespace boost::filesystem;
164
165 // Check if the path is valid. In case of error, IsValidPath will log an error message
166 if (!IsPathValid(backendPath))
167 {
168 continue;
169 }
170
Matteo Martincighe54aa062019-08-05 14:12:11 +0100171 // Get all the files in the current path in alphabetical order
172 std::vector<path> backendPathFiles;
173 std::copy(directory_iterator(backendPath), directory_iterator(), std::back_inserter(backendPathFiles));
174 std::sort(backendPathFiles.begin(), backendPathFiles.end());
175
Jan Eilers4a539fc2019-07-25 17:08:37 +0100176 // Go through all the files in the current backend path
Matteo Martincighe54aa062019-08-05 14:12:11 +0100177 for (const path& backendPathFile : backendPathFiles)
Jan Eilers4a539fc2019-07-25 17:08:37 +0100178 {
Matteo Martincighe54aa062019-08-05 14:12:11 +0100179 // Get only the name of the file (without the full path)
180 std::string filename = backendPathFile.filename().string();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100181
182 if (filename.empty())
183 {
184 // Empty filename
185 continue;
186 }
187
188 path canonicalPath;
189 try
190 {
191 // Get the canonical path for the current file, it will throw if for example the file is a
192 // symlink that cannot be resolved
Matteo Martincighe54aa062019-08-05 14:12:11 +0100193 canonicalPath = canonical(backendPathFile);
Jan Eilers4a539fc2019-07-25 17:08:37 +0100194 }
195 catch (const filesystem_error& e)
196 {
197 BOOST_LOG_TRIVIAL(warning) << "GetSharedObjects warning: " << e.what();
198 }
199 if (canonicalPath.empty())
200 {
201 // No such file or perhaps a symlink that couldn't be resolved
202 continue;
203 }
204
205 // Check if the current filename matches the expected naming convention
206 // The expected format is: <vendor>_<name>_backend.so[<version>]
207 // e.g. "Arm_GpuAcc_backend.so" or "Arm_GpuAcc_backend.so.1.2"
208 const std::regex dynamicBackendRegex("^[a-zA-Z0-9]+_[a-zA-Z0-9]+_backend.so(\\.[0-9]+)*$");
209
210 bool filenameMatch = false;
211 try
212 {
213 // Match the filename to the expected naming scheme
214 filenameMatch = std::regex_match(filename, dynamicBackendRegex);
215 }
216 catch (const std::exception& e)
217 {
218 BOOST_LOG_TRIVIAL(warning) << "GetSharedObjects warning: " << e.what();
219 }
220 if (!filenameMatch)
221 {
222 // Filename does not match the expected naming scheme (or an error has occurred)
223 continue;
224 }
225
226 // Append the valid canonical path to the output list only if it's not a duplicate
227 std::string validCanonicalPath = canonicalPath.string();
228 auto it = uniqueSharedObjects.find(validCanonicalPath);
229 if (it == uniqueSharedObjects.end())
230 {
231 // Not a duplicate, append the canonical path to the output list
232 sharedObjects.push_back(validCanonicalPath);
233
234 // Add the canonical path to the collection of unique shared objects
235 uniqueSharedObjects.insert(validCanonicalPath);
236 }
237 }
238 }
239
240 return sharedObjects;
241}
242
Matteo Martincighe54aa062019-08-05 14:12:11 +0100243std::vector<DynamicBackendPtr> DynamicBackendUtils::CreateDynamicBackends(const std::vector<std::string>& sharedObjects)
244{
245 // Create a list of dynamic backends
246 std::vector<DynamicBackendPtr> dynamicBackends;
247 for (const std::string& sharedObject : sharedObjects)
248 {
249 // Create a handle to the shared object
250 void* sharedObjectHandle = nullptr;
251 try
252 {
253 sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObject);
254 }
255 catch (const RuntimeException& e)
256 {
257 BOOST_LOG_TRIVIAL(warning) << "Cannot create a handle to the shared object file \""
258 << sharedObject << "\": " << e.what();
259 continue;
260 }
261 if (!sharedObjectHandle)
262 {
263 BOOST_LOG_TRIVIAL(warning) << "Invalid handle to the shared object file \"" << sharedObject << "\"";
264
265 continue;
266 }
267
268 // Create a dynamic backend object
269 DynamicBackendPtr dynamicBackend;
270 try
271 {
272 dynamicBackend.reset(new DynamicBackend(sharedObjectHandle));
273 }
274 catch (const Exception& e)
275 {
276 BOOST_LOG_TRIVIAL(warning) << "Cannot create a valid dynamic backend from the shared object file \""
277 << sharedObject << "\": " << e.what();
278 continue;
279 }
280 if (!dynamicBackend)
281 {
282 BOOST_LOG_TRIVIAL(warning) << "Invalid dynamic backend object for the shared object file \""
283 << sharedObject << "\"";
Matteo Martincighe54aa062019-08-05 14:12:11 +0100284 continue;
285 }
286
287 // Append the newly created dynamic backend to the list
288 dynamicBackends.push_back(std::move(dynamicBackend));
289 }
290
291 return dynamicBackends;
292}
293
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100294void DynamicBackendUtils::RegisterDynamicBackends(const std::vector<DynamicBackendPtr>& dynamicBackends)
295{
296 // Get a reference of the backend registry
297 BackendRegistry& backendRegistry = BackendRegistryInstance();
298
299 // Register the dynamic backends in the backend registry
300 RegisterDynamicBackendsImpl(backendRegistry, dynamicBackends);
301}
302
303void DynamicBackendUtils::RegisterDynamicBackendsImpl(BackendRegistry& backendRegistry,
304 const std::vector<DynamicBackendPtr>& dynamicBackends)
305{
306 // Register the dynamic backends in the backend registry
307 for (const DynamicBackendPtr& dynamicBackend : dynamicBackends)
308 {
309 BackendId dynamicBackendId;
310 try
311 {
312 dynamicBackendId = dynamicBackend->GetBackendId();
313 }
314 catch (const RuntimeException& e)
315 {
316 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend, "
317 << "an error has occurred when getting the backend id: " << e.what();
318 continue;
319 }
320 if (dynamicBackendId.IsEmpty() ||
321 dynamicBackendId.IsUndefined())
322 {
323 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend, invalid backend id: " << dynamicBackendId;
324 continue;
325 }
326
327 // Check whether the dynamic backend is already registered
328 bool backendAlreadyRegistered = backendRegistry.IsBackendRegistered(dynamicBackendId);
329 if (backendAlreadyRegistered)
330 {
331 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
332 << "\": backend already registered";
333 continue;
334 }
335
336 // Get the dynamic backend factory function
337 BackendRegistry::FactoryFunction dynamicBackendFactoryFunction = nullptr;
338 try
339 {
340 dynamicBackendFactoryFunction = dynamicBackend->GetFactoryFunction();
341 }
342 catch (const RuntimeException& e)
343 {
344 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
345 << "\": an error has occurred when getting the backend factory function: "
346 << e.what();
347 continue;
348 }
349 if (dynamicBackendFactoryFunction == nullptr)
350 {
351 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
352 << "\": invalid backend factory function";
353 continue;
354 }
355
356 // Register the dynamic backend
357 backendRegistry.Register(dynamicBackendId, dynamicBackendFactoryFunction);
358 }
359}
360
Matteo Martincighd73cecb2019-07-24 09:15:00 +0100361} // namespace armnn