blob: 8850c0f19f8ad15170c425f85af617f98945c0d7 [file] [log] [blame]
Matteo Martincighd73cecb2019-07-24 09:15:00 +01001//
Jim Flynn6da6a452020-07-14 14:26:27 +01002// Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
Matteo Martincighd73cecb2019-07-24 09:15:00 +01003// 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>
David Monahana8837bf2020-04-16 10:01:56 +01008#include "armnn/utility/StringUtils.hpp"
Francis Murtagh532a29d2020-06-29 11:50:01 +01009#include <Filesystem.hpp>
Matteo Martincighe7d44982019-08-05 12:16:47 +010010
Jan Eilers4a539fc2019-07-25 17:08:37 +010011#include <regex>
12
Matteo Martincighd73cecb2019-07-24 09:15:00 +010013namespace armnn
14{
15
16void* DynamicBackendUtils::OpenHandle(const std::string& sharedObjectPath)
17{
Jim Flynn6da6a452020-07-14 14:26:27 +010018#if defined(__unix__) || defined(__APPLE__)
Matteo Martincighd73cecb2019-07-24 09:15:00 +010019 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 {
James Ward47fce872020-09-10 11:57:28 +010027 throw RuntimeException(fmt::format("OpenHandle error: {}", GetDlError()));
Matteo Martincighd73cecb2019-07-24 09:15:00 +010028 }
29
30 return sharedObjectHandle;
Rob Hughes91e1d892019-08-23 10:11:58 +010031#else
32 throw RuntimeException("Dynamic backends not supported on this platform");
33#endif
Matteo Martincighd73cecb2019-07-24 09:15:00 +010034}
35
36void DynamicBackendUtils::CloseHandle(const void* sharedObjectHandle)
37{
Jim Flynn6da6a452020-07-14 14:26:27 +010038#if defined(__unix__) || defined(__APPLE__)
Matteo Martincighd73cecb2019-07-24 09:15:00 +010039 if (!sharedObjectHandle)
40 {
41 return;
42 }
43
44 dlclose(const_cast<void*>(sharedObjectHandle));
Rob Hughes91e1d892019-08-23 10:11:58 +010045#else
46 throw RuntimeException("Dynamic backends not supported on this platform");
47#endif
Matteo Martincighd73cecb2019-07-24 09:15:00 +010048}
49
Matteo Martincighac60d282019-07-25 15:25:44 +010050bool DynamicBackendUtils::IsBackendCompatible(const BackendVersion &backendVersion)
51{
52 BackendVersion backendApiVersion = IBackendInternal::GetApiVersion();
53
54 return IsBackendCompatibleImpl(backendApiVersion, backendVersion);
55}
56
57bool DynamicBackendUtils::IsBackendCompatibleImpl(const BackendVersion &backendApiVersion,
58 const BackendVersion &backendVersion)
59{
60 return backendVersion.m_Major == backendApiVersion.m_Major &&
61 backendVersion.m_Minor <= backendApiVersion.m_Minor;
62}
63
Matteo Martincighd73cecb2019-07-24 09:15:00 +010064std::string DynamicBackendUtils::GetDlError()
65{
Jim Flynn6da6a452020-07-14 14:26:27 +010066#if defined(__unix__) || defined(__APPLE__)
Matteo Martincighd73cecb2019-07-24 09:15:00 +010067 const char* errorMessage = dlerror();
68 if (!errorMessage)
69 {
70 return "";
71 }
72
73 return std::string(errorMessage);
Rob Hughes91e1d892019-08-23 10:11:58 +010074#else
75 throw RuntimeException("Dynamic backends not supported on this platform");
76#endif
Matteo Martincighd73cecb2019-07-24 09:15:00 +010077}
78
Matteo Martincighe7d44982019-08-05 12:16:47 +010079std::vector<std::string> DynamicBackendUtils::GetBackendPaths(const std::string& overrideBackendPath)
80{
81 // Check if a path where to dynamically load the backends from is given
82 if (!overrideBackendPath.empty())
83 {
84 if (!IsPathValid(overrideBackendPath))
85 {
Derek Lamberti08446972019-11-26 16:38:31 +000086 ARMNN_LOG(warning) << "WARNING: The given override path for dynamic backends \""
87 << overrideBackendPath << "\" is not valid";
Matteo Martincighe7d44982019-08-05 12:16:47 +010088
89 return {};
90 }
91
92 return std::vector<std::string>{ overrideBackendPath };
93 }
94
95 // Expects a colon-separated list: DYNAMIC_BACKEND_PATHS="PATH_1:PATH_2:...:PATH_N"
96 const std::string backendPaths = DYNAMIC_BACKEND_PATHS;
97
98 return GetBackendPathsImpl(backendPaths);
99}
100
101std::vector<std::string> DynamicBackendUtils::GetBackendPathsImpl(const std::string& backendPaths)
102{
Matteo Martincighf97a5de2019-08-12 14:07:59 +0100103 // Check if there's any path to process at all
104 if (backendPaths.empty())
105 {
106 // Silently return without issuing a warning as no paths have been passed, so
107 // the whole dynamic backend loading feature can be considered as disabled
108 return {};
109 }
110
Matteo Martincighe7d44982019-08-05 12:16:47 +0100111 std::unordered_set<std::string> uniqueBackendPaths;
Matteo Martincighe7d44982019-08-05 12:16:47 +0100112 std::vector<std::string> validBackendPaths;
113
114 // Split the given list of paths
David Monahana8837bf2020-04-16 10:01:56 +0100115 std::vector<std::string> tempBackendPaths = armnn::stringUtils::StringTokenizer(backendPaths, ":");
Matteo Martincighe7d44982019-08-05 12:16:47 +0100116
117 for (const std::string& path : tempBackendPaths)
118 {
119 // Check whether the path is valid
120 if (!IsPathValid(path))
121 {
122 continue;
123 }
124
125 // Check whether the path is a duplicate
126 auto it = uniqueBackendPaths.find(path);
127 if (it != uniqueBackendPaths.end())
128 {
129 // The path is a duplicate
130 continue;
131 }
132
133 // Add the path to the set of unique paths
134 uniqueBackendPaths.insert(path);
135
136 // Add the path to the list of valid paths
137 validBackendPaths.push_back(path);
138 }
139
140 return validBackendPaths;
141}
142
143bool DynamicBackendUtils::IsPathValid(const std::string& path)
144{
145 if (path.empty())
146 {
Derek Lamberti08446972019-11-26 16:38:31 +0000147 ARMNN_LOG(warning) << "WARNING: The given backend path is empty";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100148 return false;
149 }
150
Francis Murtagh532a29d2020-06-29 11:50:01 +0100151 fs::path fsPath(path);
Matteo Martincighe7d44982019-08-05 12:16:47 +0100152
Francis Murtagh532a29d2020-06-29 11:50:01 +0100153 if (!fs::exists(fsPath))
Matteo Martincighe7d44982019-08-05 12:16:47 +0100154 {
Derek Lamberti08446972019-11-26 16:38:31 +0000155 ARMNN_LOG(warning) << "WARNING: The given backend path \"" << path << "\" does not exist";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100156 return false;
157 }
158
Francis Murtagh532a29d2020-06-29 11:50:01 +0100159 if (!fs::is_directory(fsPath))
Matteo Martincighe7d44982019-08-05 12:16:47 +0100160 {
Derek Lamberti08446972019-11-26 16:38:31 +0000161 ARMNN_LOG(warning) << "WARNING: The given backend path \"" << path << "\" is not a directory";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100162 return false;
163 }
164
Francis Murtagh532a29d2020-06-29 11:50:01 +0100165 if (!fsPath.is_absolute())
Matteo Martincighe7d44982019-08-05 12:16:47 +0100166 {
Derek Lamberti08446972019-11-26 16:38:31 +0000167 ARMNN_LOG(warning) << "WARNING: The given backend path \"" << path << "\" is not absolute";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100168 return false;
169 }
170
171 return true;
172}
173
Jan Eilers4a539fc2019-07-25 17:08:37 +0100174std::vector<std::string> DynamicBackendUtils::GetSharedObjects(const std::vector<std::string>& backendPaths)
175{
176 std::unordered_set<std::string> uniqueSharedObjects;
177 std::vector<std::string> sharedObjects;
178
179 for (const std::string& backendPath : backendPaths)
180 {
Francis Murtagh532a29d2020-06-29 11:50:01 +0100181 using namespace fs;
Jan Eilers4a539fc2019-07-25 17:08:37 +0100182
183 // Check if the path is valid. In case of error, IsValidPath will log an error message
184 if (!IsPathValid(backendPath))
185 {
186 continue;
187 }
188
Matteo Martincighe54aa062019-08-05 14:12:11 +0100189 // Get all the files in the current path in alphabetical order
190 std::vector<path> backendPathFiles;
191 std::copy(directory_iterator(backendPath), directory_iterator(), std::back_inserter(backendPathFiles));
192 std::sort(backendPathFiles.begin(), backendPathFiles.end());
193
Jan Eilers4a539fc2019-07-25 17:08:37 +0100194 // Go through all the files in the current backend path
Matteo Martincighe54aa062019-08-05 14:12:11 +0100195 for (const path& backendPathFile : backendPathFiles)
Jan Eilers4a539fc2019-07-25 17:08:37 +0100196 {
Matteo Martincighe54aa062019-08-05 14:12:11 +0100197 // Get only the name of the file (without the full path)
198 std::string filename = backendPathFile.filename().string();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100199
200 if (filename.empty())
201 {
202 // Empty filename
203 continue;
204 }
205
206 path canonicalPath;
207 try
208 {
209 // Get the canonical path for the current file, it will throw if for example the file is a
210 // symlink that cannot be resolved
Matteo Martincighe54aa062019-08-05 14:12:11 +0100211 canonicalPath = canonical(backendPathFile);
Jan Eilers4a539fc2019-07-25 17:08:37 +0100212 }
213 catch (const filesystem_error& e)
214 {
Derek Lamberti08446972019-11-26 16:38:31 +0000215 ARMNN_LOG(warning) << "GetSharedObjects warning: " << e.what();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100216 }
217 if (canonicalPath.empty())
218 {
219 // No such file or perhaps a symlink that couldn't be resolved
220 continue;
221 }
222
223 // Check if the current filename matches the expected naming convention
224 // The expected format is: <vendor>_<name>_backend.so[<version>]
225 // e.g. "Arm_GpuAcc_backend.so" or "Arm_GpuAcc_backend.so.1.2"
226 const std::regex dynamicBackendRegex("^[a-zA-Z0-9]+_[a-zA-Z0-9]+_backend.so(\\.[0-9]+)*$");
227
228 bool filenameMatch = false;
229 try
230 {
231 // Match the filename to the expected naming scheme
232 filenameMatch = std::regex_match(filename, dynamicBackendRegex);
233 }
234 catch (const std::exception& e)
235 {
Derek Lamberti08446972019-11-26 16:38:31 +0000236 ARMNN_LOG(warning) << "GetSharedObjects warning: " << e.what();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100237 }
238 if (!filenameMatch)
239 {
240 // Filename does not match the expected naming scheme (or an error has occurred)
241 continue;
242 }
243
244 // Append the valid canonical path to the output list only if it's not a duplicate
245 std::string validCanonicalPath = canonicalPath.string();
246 auto it = uniqueSharedObjects.find(validCanonicalPath);
247 if (it == uniqueSharedObjects.end())
248 {
249 // Not a duplicate, append the canonical path to the output list
250 sharedObjects.push_back(validCanonicalPath);
251
252 // Add the canonical path to the collection of unique shared objects
253 uniqueSharedObjects.insert(validCanonicalPath);
254 }
255 }
256 }
257
258 return sharedObjects;
259}
260
Matteo Martincighe54aa062019-08-05 14:12:11 +0100261std::vector<DynamicBackendPtr> DynamicBackendUtils::CreateDynamicBackends(const std::vector<std::string>& sharedObjects)
262{
263 // Create a list of dynamic backends
264 std::vector<DynamicBackendPtr> dynamicBackends;
265 for (const std::string& sharedObject : sharedObjects)
266 {
267 // Create a handle to the shared object
268 void* sharedObjectHandle = nullptr;
269 try
270 {
271 sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObject);
272 }
273 catch (const RuntimeException& e)
274 {
Derek Lamberti08446972019-11-26 16:38:31 +0000275 ARMNN_LOG(warning) << "Cannot create a handle to the shared object file \""
276 << sharedObject << "\": " << e.what();
Matteo Martincighe54aa062019-08-05 14:12:11 +0100277 continue;
278 }
279 if (!sharedObjectHandle)
280 {
Derek Lamberti08446972019-11-26 16:38:31 +0000281 ARMNN_LOG(warning) << "Invalid handle to the shared object file \"" << sharedObject << "\"";
Matteo Martincighe54aa062019-08-05 14:12:11 +0100282
283 continue;
284 }
285
286 // Create a dynamic backend object
287 DynamicBackendPtr dynamicBackend;
288 try
289 {
290 dynamicBackend.reset(new DynamicBackend(sharedObjectHandle));
291 }
292 catch (const Exception& e)
293 {
Derek Lamberti08446972019-11-26 16:38:31 +0000294 ARMNN_LOG(warning) << "Cannot create a valid dynamic backend from the shared object file \""
295 << sharedObject << "\": " << e.what();
Matteo Martincighe54aa062019-08-05 14:12:11 +0100296 continue;
297 }
298 if (!dynamicBackend)
299 {
Derek Lamberti08446972019-11-26 16:38:31 +0000300 ARMNN_LOG(warning) << "Invalid dynamic backend object for the shared object file \""
301 << sharedObject << "\"";
Matteo Martincighe54aa062019-08-05 14:12:11 +0100302 continue;
303 }
304
305 // Append the newly created dynamic backend to the list
306 dynamicBackends.push_back(std::move(dynamicBackend));
307 }
308
309 return dynamicBackends;
310}
311
Narumol Prangnawarat60a20fb2019-12-09 17:24:41 +0000312void DynamicBackendUtils::DeregisterDynamicBackends(const BackendIdSet& dynamicBackends)
313{
314 // Get a reference of the backend registry
315 BackendRegistry& backendRegistry = BackendRegistryInstance();
316
317 for (const auto& id : dynamicBackends)
318 {
319 backendRegistry.Deregister(id);
320 }
321
322}
323
Matteo Martincigh89533902019-08-15 12:08:06 +0100324BackendIdSet DynamicBackendUtils::RegisterDynamicBackends(const std::vector<DynamicBackendPtr>& dynamicBackends)
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100325{
326 // Get a reference of the backend registry
327 BackendRegistry& backendRegistry = BackendRegistryInstance();
328
Matteo Martincigh89533902019-08-15 12:08:06 +0100329 // Register the dynamic backends in the backend registry, and return a list of registered backend ids
330 return RegisterDynamicBackendsImpl(backendRegistry, dynamicBackends);
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100331}
332
Matteo Martincigh89533902019-08-15 12:08:06 +0100333BackendIdSet DynamicBackendUtils::RegisterDynamicBackendsImpl(BackendRegistry& backendRegistry,
334 const std::vector<DynamicBackendPtr>& dynamicBackends)
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100335{
Matteo Martincigh89533902019-08-15 12:08:06 +0100336 // Initialize the list of registered backend ids
337 BackendIdSet registeredBackendIds;
338
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100339 // Register the dynamic backends in the backend registry
340 for (const DynamicBackendPtr& dynamicBackend : dynamicBackends)
341 {
Matteo Martincigh89533902019-08-15 12:08:06 +0100342 // Get the id of the dynamic backend
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100343 BackendId dynamicBackendId;
344 try
345 {
346 dynamicBackendId = dynamicBackend->GetBackendId();
347 }
348 catch (const RuntimeException& e)
349 {
Derek Lamberti08446972019-11-26 16:38:31 +0000350 ARMNN_LOG(warning) << "Cannot register dynamic backend, "
351 << "an error has occurred when getting the backend id: " << e.what();
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100352 continue;
353 }
354 if (dynamicBackendId.IsEmpty() ||
355 dynamicBackendId.IsUndefined())
356 {
Derek Lamberti08446972019-11-26 16:38:31 +0000357 ARMNN_LOG(warning) << "Cannot register dynamic backend, invalid backend id: " << dynamicBackendId;
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100358 continue;
359 }
360
361 // Check whether the dynamic backend is already registered
362 bool backendAlreadyRegistered = backendRegistry.IsBackendRegistered(dynamicBackendId);
363 if (backendAlreadyRegistered)
364 {
Derek Lamberti08446972019-11-26 16:38:31 +0000365 ARMNN_LOG(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
366 << "\": backend already registered";
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100367 continue;
368 }
369
370 // Get the dynamic backend factory function
371 BackendRegistry::FactoryFunction dynamicBackendFactoryFunction = nullptr;
372 try
373 {
374 dynamicBackendFactoryFunction = dynamicBackend->GetFactoryFunction();
375 }
376 catch (const RuntimeException& e)
377 {
Derek Lamberti08446972019-11-26 16:38:31 +0000378 ARMNN_LOG(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
379 << "\": an error has occurred when getting the backend factory function: "
380 << e.what();
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100381 continue;
382 }
383 if (dynamicBackendFactoryFunction == nullptr)
384 {
Derek Lamberti08446972019-11-26 16:38:31 +0000385 ARMNN_LOG(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
386 << "\": invalid backend factory function";
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100387 continue;
388 }
389
390 // Register the dynamic backend
Matteo Martincigh89533902019-08-15 12:08:06 +0100391 try
392 {
393 backendRegistry.Register(dynamicBackendId, dynamicBackendFactoryFunction);
394 }
395 catch (const InvalidArgumentException& e)
396 {
Derek Lamberti08446972019-11-26 16:38:31 +0000397 ARMNN_LOG(warning) << "An error has occurred when registering the dynamic backend \""
398 << dynamicBackendId << "\": " << e.what();
Matteo Martincigh89533902019-08-15 12:08:06 +0100399 continue;
400 }
401
402 // Add the id of the dynamic backend just registered to the list of registered backend ids
403 registeredBackendIds.insert(dynamicBackendId);
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100404 }
Matteo Martincigh89533902019-08-15 12:08:06 +0100405
406 return registeredBackendIds;
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100407}
408
Matteo Martincighd73cecb2019-07-24 09:15:00 +0100409} // namespace armnn