blob: da7c3244f1389ad0f6ce4e449f5962e4b6e4f956 [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{
Rob Hughes91e1d892019-08-23 10:11:58 +010019#if defined(__unix__)
Matteo Martincighd73cecb2019-07-24 09:15:00 +010020 if (sharedObjectPath.empty())
21 {
22 throw RuntimeException("OpenHandle error: shared object path must not be empty");
23 }
24
Matteo Martincighe54aa062019-08-05 14:12:11 +010025 void* sharedObjectHandle = dlopen(sharedObjectPath.c_str(), RTLD_LAZY);
Matteo Martincighd73cecb2019-07-24 09:15:00 +010026 if (!sharedObjectHandle)
27 {
Matteo Martincighb19d2e92019-07-25 14:04:40 +010028 throw RuntimeException(boost::str(boost::format("OpenHandle error: %1%") % GetDlError()));
Matteo Martincighd73cecb2019-07-24 09:15:00 +010029 }
30
31 return sharedObjectHandle;
Rob Hughes91e1d892019-08-23 10:11:58 +010032#else
33 throw RuntimeException("Dynamic backends not supported on this platform");
34#endif
Matteo Martincighd73cecb2019-07-24 09:15:00 +010035}
36
37void DynamicBackendUtils::CloseHandle(const void* sharedObjectHandle)
38{
Rob Hughes91e1d892019-08-23 10:11:58 +010039#if defined(__unix__)
Matteo Martincighd73cecb2019-07-24 09:15:00 +010040 if (!sharedObjectHandle)
41 {
42 return;
43 }
44
45 dlclose(const_cast<void*>(sharedObjectHandle));
Rob Hughes91e1d892019-08-23 10:11:58 +010046#else
47 throw RuntimeException("Dynamic backends not supported on this platform");
48#endif
Matteo Martincighd73cecb2019-07-24 09:15:00 +010049}
50
Matteo Martincighac60d282019-07-25 15:25:44 +010051bool DynamicBackendUtils::IsBackendCompatible(const BackendVersion &backendVersion)
52{
53 BackendVersion backendApiVersion = IBackendInternal::GetApiVersion();
54
55 return IsBackendCompatibleImpl(backendApiVersion, backendVersion);
56}
57
58bool DynamicBackendUtils::IsBackendCompatibleImpl(const BackendVersion &backendApiVersion,
59 const BackendVersion &backendVersion)
60{
61 return backendVersion.m_Major == backendApiVersion.m_Major &&
62 backendVersion.m_Minor <= backendApiVersion.m_Minor;
63}
64
Matteo Martincighd73cecb2019-07-24 09:15:00 +010065std::string DynamicBackendUtils::GetDlError()
66{
Rob Hughes91e1d892019-08-23 10:11:58 +010067#if defined(__unix__)
Matteo Martincighd73cecb2019-07-24 09:15:00 +010068 const char* errorMessage = dlerror();
69 if (!errorMessage)
70 {
71 return "";
72 }
73
74 return std::string(errorMessage);
Rob Hughes91e1d892019-08-23 10:11:58 +010075#else
76 throw RuntimeException("Dynamic backends not supported on this platform");
77#endif
Matteo Martincighd73cecb2019-07-24 09:15:00 +010078}
79
Matteo Martincighe7d44982019-08-05 12:16:47 +010080std::vector<std::string> DynamicBackendUtils::GetBackendPaths(const std::string& overrideBackendPath)
81{
82 // Check if a path where to dynamically load the backends from is given
83 if (!overrideBackendPath.empty())
84 {
85 if (!IsPathValid(overrideBackendPath))
86 {
87 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given override path for dynamic backends \""
88 << overrideBackendPath << "\" is not valid";
89
90 return {};
91 }
92
93 return std::vector<std::string>{ overrideBackendPath };
94 }
95
96 // Expects a colon-separated list: DYNAMIC_BACKEND_PATHS="PATH_1:PATH_2:...:PATH_N"
97 const std::string backendPaths = DYNAMIC_BACKEND_PATHS;
98
99 return GetBackendPathsImpl(backendPaths);
100}
101
102std::vector<std::string> DynamicBackendUtils::GetBackendPathsImpl(const std::string& backendPaths)
103{
Matteo Martincighf97a5de2019-08-12 14:07:59 +0100104 // Check if there's any path to process at all
105 if (backendPaths.empty())
106 {
107 // Silently return without issuing a warning as no paths have been passed, so
108 // the whole dynamic backend loading feature can be considered as disabled
109 return {};
110 }
111
Matteo Martincighe7d44982019-08-05 12:16:47 +0100112 std::unordered_set<std::string> uniqueBackendPaths;
113 std::vector<std::string> tempBackendPaths;
114 std::vector<std::string> validBackendPaths;
115
116 // Split the given list of paths
117 boost::split(tempBackendPaths, backendPaths, boost::is_any_of(":"));
118
119 for (const std::string& path : tempBackendPaths)
120 {
121 // Check whether the path is valid
122 if (!IsPathValid(path))
123 {
124 continue;
125 }
126
127 // Check whether the path is a duplicate
128 auto it = uniqueBackendPaths.find(path);
129 if (it != uniqueBackendPaths.end())
130 {
131 // The path is a duplicate
132 continue;
133 }
134
135 // Add the path to the set of unique paths
136 uniqueBackendPaths.insert(path);
137
138 // Add the path to the list of valid paths
139 validBackendPaths.push_back(path);
140 }
141
142 return validBackendPaths;
143}
144
145bool DynamicBackendUtils::IsPathValid(const std::string& path)
146{
147 if (path.empty())
148 {
149 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path is empty";
150 return false;
151 }
152
153 boost::filesystem::path boostPath(path);
154
155 if (!boost::filesystem::exists(boostPath))
156 {
157 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" does not exist";
158 return false;
159 }
160
161 if (!boost::filesystem::is_directory(boostPath))
162 {
163 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" is not a directory";
164 return false;
165 }
166
167 if (!boostPath.is_absolute())
168 {
169 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" is not absolute";
170 return false;
171 }
172
173 return true;
174}
175
Jan Eilers4a539fc2019-07-25 17:08:37 +0100176std::vector<std::string> DynamicBackendUtils::GetSharedObjects(const std::vector<std::string>& backendPaths)
177{
178 std::unordered_set<std::string> uniqueSharedObjects;
179 std::vector<std::string> sharedObjects;
180
181 for (const std::string& backendPath : backendPaths)
182 {
183 using namespace boost::filesystem;
184
185 // Check if the path is valid. In case of error, IsValidPath will log an error message
186 if (!IsPathValid(backendPath))
187 {
188 continue;
189 }
190
Matteo Martincighe54aa062019-08-05 14:12:11 +0100191 // Get all the files in the current path in alphabetical order
192 std::vector<path> backendPathFiles;
193 std::copy(directory_iterator(backendPath), directory_iterator(), std::back_inserter(backendPathFiles));
194 std::sort(backendPathFiles.begin(), backendPathFiles.end());
195
Jan Eilers4a539fc2019-07-25 17:08:37 +0100196 // Go through all the files in the current backend path
Matteo Martincighe54aa062019-08-05 14:12:11 +0100197 for (const path& backendPathFile : backendPathFiles)
Jan Eilers4a539fc2019-07-25 17:08:37 +0100198 {
Matteo Martincighe54aa062019-08-05 14:12:11 +0100199 // Get only the name of the file (without the full path)
200 std::string filename = backendPathFile.filename().string();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100201
202 if (filename.empty())
203 {
204 // Empty filename
205 continue;
206 }
207
208 path canonicalPath;
209 try
210 {
211 // Get the canonical path for the current file, it will throw if for example the file is a
212 // symlink that cannot be resolved
Matteo Martincighe54aa062019-08-05 14:12:11 +0100213 canonicalPath = canonical(backendPathFile);
Jan Eilers4a539fc2019-07-25 17:08:37 +0100214 }
215 catch (const filesystem_error& e)
216 {
217 BOOST_LOG_TRIVIAL(warning) << "GetSharedObjects warning: " << e.what();
218 }
219 if (canonicalPath.empty())
220 {
221 // No such file or perhaps a symlink that couldn't be resolved
222 continue;
223 }
224
225 // Check if the current filename matches the expected naming convention
226 // The expected format is: <vendor>_<name>_backend.so[<version>]
227 // e.g. "Arm_GpuAcc_backend.so" or "Arm_GpuAcc_backend.so.1.2"
228 const std::regex dynamicBackendRegex("^[a-zA-Z0-9]+_[a-zA-Z0-9]+_backend.so(\\.[0-9]+)*$");
229
230 bool filenameMatch = false;
231 try
232 {
233 // Match the filename to the expected naming scheme
234 filenameMatch = std::regex_match(filename, dynamicBackendRegex);
235 }
236 catch (const std::exception& e)
237 {
238 BOOST_LOG_TRIVIAL(warning) << "GetSharedObjects warning: " << e.what();
239 }
240 if (!filenameMatch)
241 {
242 // Filename does not match the expected naming scheme (or an error has occurred)
243 continue;
244 }
245
246 // Append the valid canonical path to the output list only if it's not a duplicate
247 std::string validCanonicalPath = canonicalPath.string();
248 auto it = uniqueSharedObjects.find(validCanonicalPath);
249 if (it == uniqueSharedObjects.end())
250 {
251 // Not a duplicate, append the canonical path to the output list
252 sharedObjects.push_back(validCanonicalPath);
253
254 // Add the canonical path to the collection of unique shared objects
255 uniqueSharedObjects.insert(validCanonicalPath);
256 }
257 }
258 }
259
260 return sharedObjects;
261}
262
Matteo Martincighe54aa062019-08-05 14:12:11 +0100263std::vector<DynamicBackendPtr> DynamicBackendUtils::CreateDynamicBackends(const std::vector<std::string>& sharedObjects)
264{
265 // Create a list of dynamic backends
266 std::vector<DynamicBackendPtr> dynamicBackends;
267 for (const std::string& sharedObject : sharedObjects)
268 {
269 // Create a handle to the shared object
270 void* sharedObjectHandle = nullptr;
271 try
272 {
273 sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObject);
274 }
275 catch (const RuntimeException& e)
276 {
277 BOOST_LOG_TRIVIAL(warning) << "Cannot create a handle to the shared object file \""
278 << sharedObject << "\": " << e.what();
279 continue;
280 }
281 if (!sharedObjectHandle)
282 {
283 BOOST_LOG_TRIVIAL(warning) << "Invalid handle to the shared object file \"" << sharedObject << "\"";
284
285 continue;
286 }
287
288 // Create a dynamic backend object
289 DynamicBackendPtr dynamicBackend;
290 try
291 {
292 dynamicBackend.reset(new DynamicBackend(sharedObjectHandle));
293 }
294 catch (const Exception& e)
295 {
296 BOOST_LOG_TRIVIAL(warning) << "Cannot create a valid dynamic backend from the shared object file \""
297 << sharedObject << "\": " << e.what();
298 continue;
299 }
300 if (!dynamicBackend)
301 {
302 BOOST_LOG_TRIVIAL(warning) << "Invalid dynamic backend object for the shared object file \""
303 << sharedObject << "\"";
Matteo Martincighe54aa062019-08-05 14:12:11 +0100304 continue;
305 }
306
307 // Append the newly created dynamic backend to the list
308 dynamicBackends.push_back(std::move(dynamicBackend));
309 }
310
311 return dynamicBackends;
312}
313
Matteo Martincigh89533902019-08-15 12:08:06 +0100314BackendIdSet DynamicBackendUtils::RegisterDynamicBackends(const std::vector<DynamicBackendPtr>& dynamicBackends)
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100315{
316 // Get a reference of the backend registry
317 BackendRegistry& backendRegistry = BackendRegistryInstance();
318
Matteo Martincigh89533902019-08-15 12:08:06 +0100319 // Register the dynamic backends in the backend registry, and return a list of registered backend ids
320 return RegisterDynamicBackendsImpl(backendRegistry, dynamicBackends);
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100321}
322
Matteo Martincigh89533902019-08-15 12:08:06 +0100323BackendIdSet DynamicBackendUtils::RegisterDynamicBackendsImpl(BackendRegistry& backendRegistry,
324 const std::vector<DynamicBackendPtr>& dynamicBackends)
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100325{
Matteo Martincigh89533902019-08-15 12:08:06 +0100326 // Initialize the list of registered backend ids
327 BackendIdSet registeredBackendIds;
328
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100329 // Register the dynamic backends in the backend registry
330 for (const DynamicBackendPtr& dynamicBackend : dynamicBackends)
331 {
Matteo Martincigh89533902019-08-15 12:08:06 +0100332 // Get the id of the dynamic backend
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100333 BackendId dynamicBackendId;
334 try
335 {
336 dynamicBackendId = dynamicBackend->GetBackendId();
337 }
338 catch (const RuntimeException& e)
339 {
340 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend, "
341 << "an error has occurred when getting the backend id: " << e.what();
342 continue;
343 }
344 if (dynamicBackendId.IsEmpty() ||
345 dynamicBackendId.IsUndefined())
346 {
347 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend, invalid backend id: " << dynamicBackendId;
348 continue;
349 }
350
351 // Check whether the dynamic backend is already registered
352 bool backendAlreadyRegistered = backendRegistry.IsBackendRegistered(dynamicBackendId);
353 if (backendAlreadyRegistered)
354 {
355 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
356 << "\": backend already registered";
357 continue;
358 }
359
360 // Get the dynamic backend factory function
361 BackendRegistry::FactoryFunction dynamicBackendFactoryFunction = nullptr;
362 try
363 {
364 dynamicBackendFactoryFunction = dynamicBackend->GetFactoryFunction();
365 }
366 catch (const RuntimeException& e)
367 {
368 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
369 << "\": an error has occurred when getting the backend factory function: "
370 << e.what();
371 continue;
372 }
373 if (dynamicBackendFactoryFunction == nullptr)
374 {
375 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
376 << "\": invalid backend factory function";
377 continue;
378 }
379
380 // Register the dynamic backend
Matteo Martincigh89533902019-08-15 12:08:06 +0100381 try
382 {
383 backendRegistry.Register(dynamicBackendId, dynamicBackendFactoryFunction);
384 }
385 catch (const InvalidArgumentException& e)
386 {
387 BOOST_LOG_TRIVIAL(warning) << "An error has occurred when registering the dynamic backend \""
388 << dynamicBackendId << "\": " << e.what();
389 continue;
390 }
391
392 // Add the id of the dynamic backend just registered to the list of registered backend ids
393 registeredBackendIds.insert(dynamicBackendId);
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100394 }
Matteo Martincigh89533902019-08-15 12:08:06 +0100395
396 return registeredBackendIds;
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100397}
398
Matteo Martincighd73cecb2019-07-24 09:15:00 +0100399} // namespace armnn