blob: ea0869220fddf1c321b27dce42c2296f067cab71 [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
Matthew Benthamf48afc62020-01-15 17:55:08 +00006#include <armnn/Logging.hpp>
Matteo Martincighe5b8eb92019-11-28 15:45:42 +00007#include <backendsCommon/DynamicBackendUtils.hpp>
Matteo Martincighd73cecb2019-07-24 09:15:00 +01008
Jan Eilers4a539fc2019-07-25 17:08:37 +01009#include <boost/filesystem.hpp>
Matteo Martincighe7d44982019-08-05 12:16:47 +010010#include <boost/algorithm/string.hpp>
Matteo Martincighe7d44982019-08-05 12:16:47 +010011
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 {
Derek Lamberti08446972019-11-26 16:38:31 +000087 ARMNN_LOG(warning) << "WARNING: The given override path for dynamic backends \""
88 << overrideBackendPath << "\" is not valid";
Matteo Martincighe7d44982019-08-05 12:16:47 +010089
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 {
Derek Lamberti08446972019-11-26 16:38:31 +0000149 ARMNN_LOG(warning) << "WARNING: The given backend path is empty";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100150 return false;
151 }
152
153 boost::filesystem::path boostPath(path);
154
155 if (!boost::filesystem::exists(boostPath))
156 {
Derek Lamberti08446972019-11-26 16:38:31 +0000157 ARMNN_LOG(warning) << "WARNING: The given backend path \"" << path << "\" does not exist";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100158 return false;
159 }
160
161 if (!boost::filesystem::is_directory(boostPath))
162 {
Derek Lamberti08446972019-11-26 16:38:31 +0000163 ARMNN_LOG(warning) << "WARNING: The given backend path \"" << path << "\" is not a directory";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100164 return false;
165 }
166
167 if (!boostPath.is_absolute())
168 {
Derek Lamberti08446972019-11-26 16:38:31 +0000169 ARMNN_LOG(warning) << "WARNING: The given backend path \"" << path << "\" is not absolute";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100170 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 {
Derek Lamberti08446972019-11-26 16:38:31 +0000217 ARMNN_LOG(warning) << "GetSharedObjects warning: " << e.what();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100218 }
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 {
Derek Lamberti08446972019-11-26 16:38:31 +0000238 ARMNN_LOG(warning) << "GetSharedObjects warning: " << e.what();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100239 }
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 {
Derek Lamberti08446972019-11-26 16:38:31 +0000277 ARMNN_LOG(warning) << "Cannot create a handle to the shared object file \""
278 << sharedObject << "\": " << e.what();
Matteo Martincighe54aa062019-08-05 14:12:11 +0100279 continue;
280 }
281 if (!sharedObjectHandle)
282 {
Derek Lamberti08446972019-11-26 16:38:31 +0000283 ARMNN_LOG(warning) << "Invalid handle to the shared object file \"" << sharedObject << "\"";
Matteo Martincighe54aa062019-08-05 14:12:11 +0100284
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 {
Derek Lamberti08446972019-11-26 16:38:31 +0000296 ARMNN_LOG(warning) << "Cannot create a valid dynamic backend from the shared object file \""
297 << sharedObject << "\": " << e.what();
Matteo Martincighe54aa062019-08-05 14:12:11 +0100298 continue;
299 }
300 if (!dynamicBackend)
301 {
Derek Lamberti08446972019-11-26 16:38:31 +0000302 ARMNN_LOG(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
Narumol Prangnawarat60a20fb2019-12-09 17:24:41 +0000314void DynamicBackendUtils::DeregisterDynamicBackends(const BackendIdSet& dynamicBackends)
315{
316 // Get a reference of the backend registry
317 BackendRegistry& backendRegistry = BackendRegistryInstance();
318
319 for (const auto& id : dynamicBackends)
320 {
321 backendRegistry.Deregister(id);
322 }
323
324}
325
Matteo Martincigh89533902019-08-15 12:08:06 +0100326BackendIdSet DynamicBackendUtils::RegisterDynamicBackends(const std::vector<DynamicBackendPtr>& dynamicBackends)
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100327{
328 // Get a reference of the backend registry
329 BackendRegistry& backendRegistry = BackendRegistryInstance();
330
Matteo Martincigh89533902019-08-15 12:08:06 +0100331 // Register the dynamic backends in the backend registry, and return a list of registered backend ids
332 return RegisterDynamicBackendsImpl(backendRegistry, dynamicBackends);
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100333}
334
Matteo Martincigh89533902019-08-15 12:08:06 +0100335BackendIdSet DynamicBackendUtils::RegisterDynamicBackendsImpl(BackendRegistry& backendRegistry,
336 const std::vector<DynamicBackendPtr>& dynamicBackends)
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100337{
Matteo Martincigh89533902019-08-15 12:08:06 +0100338 // Initialize the list of registered backend ids
339 BackendIdSet registeredBackendIds;
340
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100341 // Register the dynamic backends in the backend registry
342 for (const DynamicBackendPtr& dynamicBackend : dynamicBackends)
343 {
Matteo Martincigh89533902019-08-15 12:08:06 +0100344 // Get the id of the dynamic backend
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100345 BackendId dynamicBackendId;
346 try
347 {
348 dynamicBackendId = dynamicBackend->GetBackendId();
349 }
350 catch (const RuntimeException& e)
351 {
Derek Lamberti08446972019-11-26 16:38:31 +0000352 ARMNN_LOG(warning) << "Cannot register dynamic backend, "
353 << "an error has occurred when getting the backend id: " << e.what();
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100354 continue;
355 }
356 if (dynamicBackendId.IsEmpty() ||
357 dynamicBackendId.IsUndefined())
358 {
Derek Lamberti08446972019-11-26 16:38:31 +0000359 ARMNN_LOG(warning) << "Cannot register dynamic backend, invalid backend id: " << dynamicBackendId;
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100360 continue;
361 }
362
363 // Check whether the dynamic backend is already registered
364 bool backendAlreadyRegistered = backendRegistry.IsBackendRegistered(dynamicBackendId);
365 if (backendAlreadyRegistered)
366 {
Derek Lamberti08446972019-11-26 16:38:31 +0000367 ARMNN_LOG(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
368 << "\": backend already registered";
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100369 continue;
370 }
371
372 // Get the dynamic backend factory function
373 BackendRegistry::FactoryFunction dynamicBackendFactoryFunction = nullptr;
374 try
375 {
376 dynamicBackendFactoryFunction = dynamicBackend->GetFactoryFunction();
377 }
378 catch (const RuntimeException& e)
379 {
Derek Lamberti08446972019-11-26 16:38:31 +0000380 ARMNN_LOG(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
381 << "\": an error has occurred when getting the backend factory function: "
382 << e.what();
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100383 continue;
384 }
385 if (dynamicBackendFactoryFunction == nullptr)
386 {
Derek Lamberti08446972019-11-26 16:38:31 +0000387 ARMNN_LOG(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
388 << "\": invalid backend factory function";
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100389 continue;
390 }
391
392 // Register the dynamic backend
Matteo Martincigh89533902019-08-15 12:08:06 +0100393 try
394 {
395 backendRegistry.Register(dynamicBackendId, dynamicBackendFactoryFunction);
396 }
397 catch (const InvalidArgumentException& e)
398 {
Derek Lamberti08446972019-11-26 16:38:31 +0000399 ARMNN_LOG(warning) << "An error has occurred when registering the dynamic backend \""
400 << dynamicBackendId << "\": " << e.what();
Matteo Martincigh89533902019-08-15 12:08:06 +0100401 continue;
402 }
403
404 // Add the id of the dynamic backend just registered to the list of registered backend ids
405 registeredBackendIds.insert(dynamicBackendId);
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100406 }
Matteo Martincigh89533902019-08-15 12:08:06 +0100407
408 return registeredBackendIds;
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100409}
410
Matteo Martincighd73cecb2019-07-24 09:15:00 +0100411} // namespace armnn