blob: 7e4edce8a00095c6fab1c429054ef9214f443ad8 [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"
Rob Hughes9542f902021-07-14 09:48:54 +01009#include <armnnUtils/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
Jim Flynn870b96c2022-03-25 21:24:56 +000032 armnn::IgnoreUnused(sharedObjectPath);
Rob Hughes91e1d892019-08-23 10:11:58 +010033 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{
Jim Flynn6da6a452020-07-14 14:26:27 +010039#if defined(__unix__) || defined(__APPLE__)
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
Jim Flynn870b96c2022-03-25 21:24:56 +000047 armnn::IgnoreUnused(sharedObjectHandle);
Rob Hughes91e1d892019-08-23 10:11:58 +010048 throw RuntimeException("Dynamic backends not supported on this platform");
49#endif
Matteo Martincighd73cecb2019-07-24 09:15:00 +010050}
51
Matteo Martincighac60d282019-07-25 15:25:44 +010052bool DynamicBackendUtils::IsBackendCompatible(const BackendVersion &backendVersion)
53{
54 BackendVersion backendApiVersion = IBackendInternal::GetApiVersion();
55
56 return IsBackendCompatibleImpl(backendApiVersion, backendVersion);
57}
58
59bool DynamicBackendUtils::IsBackendCompatibleImpl(const BackendVersion &backendApiVersion,
60 const BackendVersion &backendVersion)
61{
62 return backendVersion.m_Major == backendApiVersion.m_Major &&
63 backendVersion.m_Minor <= backendApiVersion.m_Minor;
64}
65
Matteo Martincighd73cecb2019-07-24 09:15:00 +010066std::string DynamicBackendUtils::GetDlError()
67{
Jim Flynn6da6a452020-07-14 14:26:27 +010068#if defined(__unix__) || defined(__APPLE__)
Matteo Martincighd73cecb2019-07-24 09:15:00 +010069 const char* errorMessage = dlerror();
70 if (!errorMessage)
71 {
72 return "";
73 }
74
75 return std::string(errorMessage);
Rob Hughes91e1d892019-08-23 10:11:58 +010076#else
77 throw RuntimeException("Dynamic backends not supported on this platform");
78#endif
Matteo Martincighd73cecb2019-07-24 09:15:00 +010079}
80
Matteo Martincighe7d44982019-08-05 12:16:47 +010081std::vector<std::string> DynamicBackendUtils::GetBackendPaths(const std::string& overrideBackendPath)
82{
83 // Check if a path where to dynamically load the backends from is given
84 if (!overrideBackendPath.empty())
85 {
86 if (!IsPathValid(overrideBackendPath))
87 {
Derek Lamberti08446972019-11-26 16:38:31 +000088 ARMNN_LOG(warning) << "WARNING: The given override path for dynamic backends \""
89 << overrideBackendPath << "\" is not valid";
Matteo Martincighe7d44982019-08-05 12:16:47 +010090
91 return {};
92 }
93
94 return std::vector<std::string>{ overrideBackendPath };
95 }
96
97 // Expects a colon-separated list: DYNAMIC_BACKEND_PATHS="PATH_1:PATH_2:...:PATH_N"
98 const std::string backendPaths = DYNAMIC_BACKEND_PATHS;
99
100 return GetBackendPathsImpl(backendPaths);
101}
102
103std::vector<std::string> DynamicBackendUtils::GetBackendPathsImpl(const std::string& backendPaths)
104{
Matteo Martincighf97a5de2019-08-12 14:07:59 +0100105 // Check if there's any path to process at all
106 if (backendPaths.empty())
107 {
108 // Silently return without issuing a warning as no paths have been passed, so
109 // the whole dynamic backend loading feature can be considered as disabled
110 return {};
111 }
112
Matteo Martincighe7d44982019-08-05 12:16:47 +0100113 std::unordered_set<std::string> uniqueBackendPaths;
Matteo Martincighe7d44982019-08-05 12:16:47 +0100114 std::vector<std::string> validBackendPaths;
115
116 // Split the given list of paths
David Monahana8837bf2020-04-16 10:01:56 +0100117 std::vector<std::string> tempBackendPaths = armnn::stringUtils::StringTokenizer(backendPaths, ":");
Matteo Martincighe7d44982019-08-05 12:16:47 +0100118
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
Jim Flynn870b96c2022-03-25 21:24:56 +0000153#if !defined(ARMNN_DISABLE_FILESYSTEM)
Francis Murtagh532a29d2020-06-29 11:50:01 +0100154 fs::path fsPath(path);
Matteo Martincighe7d44982019-08-05 12:16:47 +0100155
Francis Murtagh532a29d2020-06-29 11:50:01 +0100156 if (!fs::exists(fsPath))
Matteo Martincighe7d44982019-08-05 12:16:47 +0100157 {
Derek Lamberti08446972019-11-26 16:38:31 +0000158 ARMNN_LOG(warning) << "WARNING: The given backend path \"" << path << "\" does not exist";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100159 return false;
160 }
161
Francis Murtagh532a29d2020-06-29 11:50:01 +0100162 if (!fs::is_directory(fsPath))
Matteo Martincighe7d44982019-08-05 12:16:47 +0100163 {
Derek Lamberti08446972019-11-26 16:38:31 +0000164 ARMNN_LOG(warning) << "WARNING: The given backend path \"" << path << "\" is not a directory";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100165 return false;
166 }
167
Francis Murtagh532a29d2020-06-29 11:50:01 +0100168 if (!fsPath.is_absolute())
Matteo Martincighe7d44982019-08-05 12:16:47 +0100169 {
Derek Lamberti08446972019-11-26 16:38:31 +0000170 ARMNN_LOG(warning) << "WARNING: The given backend path \"" << path << "\" is not absolute";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100171 return false;
172 }
Jim Flynn870b96c2022-03-25 21:24:56 +0000173#endif // !defined(ARMNN_DISABLE_FILESYSTEM)
Matteo Martincighe7d44982019-08-05 12:16:47 +0100174
175 return true;
176}
177
Jan Eilers4a539fc2019-07-25 17:08:37 +0100178std::vector<std::string> DynamicBackendUtils::GetSharedObjects(const std::vector<std::string>& backendPaths)
179{
180 std::unordered_set<std::string> uniqueSharedObjects;
181 std::vector<std::string> sharedObjects;
182
Jim Flynn870b96c2022-03-25 21:24:56 +0000183#if !defined(ARMNN_DISABLE_FILESYSTEM)
Jan Eilers4a539fc2019-07-25 17:08:37 +0100184 for (const std::string& backendPath : backendPaths)
185 {
Francis Murtagh532a29d2020-06-29 11:50:01 +0100186 using namespace fs;
Jan Eilers4a539fc2019-07-25 17:08:37 +0100187
188 // Check if the path is valid. In case of error, IsValidPath will log an error message
189 if (!IsPathValid(backendPath))
190 {
191 continue;
192 }
193
Matteo Martincighe54aa062019-08-05 14:12:11 +0100194 // Get all the files in the current path in alphabetical order
195 std::vector<path> backendPathFiles;
196 std::copy(directory_iterator(backendPath), directory_iterator(), std::back_inserter(backendPathFiles));
197 std::sort(backendPathFiles.begin(), backendPathFiles.end());
198
Jan Eilers4a539fc2019-07-25 17:08:37 +0100199 // Go through all the files in the current backend path
Matteo Martincighe54aa062019-08-05 14:12:11 +0100200 for (const path& backendPathFile : backendPathFiles)
Jan Eilers4a539fc2019-07-25 17:08:37 +0100201 {
Matteo Martincighe54aa062019-08-05 14:12:11 +0100202 // Get only the name of the file (without the full path)
203 std::string filename = backendPathFile.filename().string();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100204
205 if (filename.empty())
206 {
207 // Empty filename
208 continue;
209 }
210
211 path canonicalPath;
212 try
213 {
214 // Get the canonical path for the current file, it will throw if for example the file is a
215 // symlink that cannot be resolved
Matteo Martincighe54aa062019-08-05 14:12:11 +0100216 canonicalPath = canonical(backendPathFile);
Jan Eilers4a539fc2019-07-25 17:08:37 +0100217 }
218 catch (const filesystem_error& e)
219 {
Derek Lamberti08446972019-11-26 16:38:31 +0000220 ARMNN_LOG(warning) << "GetSharedObjects warning: " << e.what();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100221 }
222 if (canonicalPath.empty())
223 {
224 // No such file or perhaps a symlink that couldn't be resolved
225 continue;
226 }
227
228 // Check if the current filename matches the expected naming convention
229 // The expected format is: <vendor>_<name>_backend.so[<version>]
230 // e.g. "Arm_GpuAcc_backend.so" or "Arm_GpuAcc_backend.so.1.2"
231 const std::regex dynamicBackendRegex("^[a-zA-Z0-9]+_[a-zA-Z0-9]+_backend.so(\\.[0-9]+)*$");
232
233 bool filenameMatch = false;
234 try
235 {
236 // Match the filename to the expected naming scheme
237 filenameMatch = std::regex_match(filename, dynamicBackendRegex);
238 }
239 catch (const std::exception& e)
240 {
Derek Lamberti08446972019-11-26 16:38:31 +0000241 ARMNN_LOG(warning) << "GetSharedObjects warning: " << e.what();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100242 }
243 if (!filenameMatch)
244 {
245 // Filename does not match the expected naming scheme (or an error has occurred)
246 continue;
247 }
248
249 // Append the valid canonical path to the output list only if it's not a duplicate
250 std::string validCanonicalPath = canonicalPath.string();
251 auto it = uniqueSharedObjects.find(validCanonicalPath);
252 if (it == uniqueSharedObjects.end())
253 {
254 // Not a duplicate, append the canonical path to the output list
255 sharedObjects.push_back(validCanonicalPath);
256
257 // Add the canonical path to the collection of unique shared objects
258 uniqueSharedObjects.insert(validCanonicalPath);
259 }
260 }
261 }
Jim Flynn870b96c2022-03-25 21:24:56 +0000262#else
263 armnn::IgnoreUnused(backendPaths);
264#endif // !defined(ARMNN_DISABLE_FILESYSTEM)
Jan Eilers4a539fc2019-07-25 17:08:37 +0100265
266 return sharedObjects;
267}
268
Matteo Martincighe54aa062019-08-05 14:12:11 +0100269std::vector<DynamicBackendPtr> DynamicBackendUtils::CreateDynamicBackends(const std::vector<std::string>& sharedObjects)
270{
271 // Create a list of dynamic backends
272 std::vector<DynamicBackendPtr> dynamicBackends;
273 for (const std::string& sharedObject : sharedObjects)
274 {
275 // Create a handle to the shared object
276 void* sharedObjectHandle = nullptr;
277 try
278 {
279 sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObject);
280 }
281 catch (const RuntimeException& e)
282 {
Derek Lamberti08446972019-11-26 16:38:31 +0000283 ARMNN_LOG(warning) << "Cannot create a handle to the shared object file \""
284 << sharedObject << "\": " << e.what();
Matteo Martincighe54aa062019-08-05 14:12:11 +0100285 continue;
286 }
287 if (!sharedObjectHandle)
288 {
Derek Lamberti08446972019-11-26 16:38:31 +0000289 ARMNN_LOG(warning) << "Invalid handle to the shared object file \"" << sharedObject << "\"";
Matteo Martincighe54aa062019-08-05 14:12:11 +0100290
291 continue;
292 }
293
294 // Create a dynamic backend object
295 DynamicBackendPtr dynamicBackend;
296 try
297 {
298 dynamicBackend.reset(new DynamicBackend(sharedObjectHandle));
299 }
300 catch (const Exception& e)
301 {
Derek Lamberti08446972019-11-26 16:38:31 +0000302 ARMNN_LOG(warning) << "Cannot create a valid dynamic backend from the shared object file \""
303 << sharedObject << "\": " << e.what();
Matteo Martincighe54aa062019-08-05 14:12:11 +0100304 continue;
305 }
306 if (!dynamicBackend)
307 {
Derek Lamberti08446972019-11-26 16:38:31 +0000308 ARMNN_LOG(warning) << "Invalid dynamic backend object for the shared object file \""
309 << sharedObject << "\"";
Matteo Martincighe54aa062019-08-05 14:12:11 +0100310 continue;
311 }
312
313 // Append the newly created dynamic backend to the list
314 dynamicBackends.push_back(std::move(dynamicBackend));
315 }
316
317 return dynamicBackends;
318}
319
Narumol Prangnawarat60a20fb2019-12-09 17:24:41 +0000320void DynamicBackendUtils::DeregisterDynamicBackends(const BackendIdSet& dynamicBackends)
321{
322 // Get a reference of the backend registry
323 BackendRegistry& backendRegistry = BackendRegistryInstance();
324
325 for (const auto& id : dynamicBackends)
326 {
327 backendRegistry.Deregister(id);
328 }
329
330}
331
Matteo Martincigh89533902019-08-15 12:08:06 +0100332BackendIdSet DynamicBackendUtils::RegisterDynamicBackends(const std::vector<DynamicBackendPtr>& dynamicBackends)
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100333{
334 // Get a reference of the backend registry
335 BackendRegistry& backendRegistry = BackendRegistryInstance();
336
Matteo Martincigh89533902019-08-15 12:08:06 +0100337 // Register the dynamic backends in the backend registry, and return a list of registered backend ids
338 return RegisterDynamicBackendsImpl(backendRegistry, dynamicBackends);
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100339}
340
Matteo Martincigh89533902019-08-15 12:08:06 +0100341BackendIdSet DynamicBackendUtils::RegisterDynamicBackendsImpl(BackendRegistry& backendRegistry,
342 const std::vector<DynamicBackendPtr>& dynamicBackends)
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100343{
Matteo Martincigh89533902019-08-15 12:08:06 +0100344 // Initialize the list of registered backend ids
345 BackendIdSet registeredBackendIds;
346
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100347 // Register the dynamic backends in the backend registry
348 for (const DynamicBackendPtr& dynamicBackend : dynamicBackends)
349 {
Matteo Martincigh89533902019-08-15 12:08:06 +0100350 // Get the id of the dynamic backend
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100351 BackendId dynamicBackendId;
352 try
353 {
354 dynamicBackendId = dynamicBackend->GetBackendId();
355 }
356 catch (const RuntimeException& e)
357 {
Derek Lamberti08446972019-11-26 16:38:31 +0000358 ARMNN_LOG(warning) << "Cannot register dynamic backend, "
359 << "an error has occurred when getting the backend id: " << e.what();
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100360 continue;
361 }
362 if (dynamicBackendId.IsEmpty() ||
363 dynamicBackendId.IsUndefined())
364 {
Derek Lamberti08446972019-11-26 16:38:31 +0000365 ARMNN_LOG(warning) << "Cannot register dynamic backend, invalid backend id: " << dynamicBackendId;
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100366 continue;
367 }
368
369 // Check whether the dynamic backend is already registered
370 bool backendAlreadyRegistered = backendRegistry.IsBackendRegistered(dynamicBackendId);
371 if (backendAlreadyRegistered)
372 {
Derek Lamberti08446972019-11-26 16:38:31 +0000373 ARMNN_LOG(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
374 << "\": backend already registered";
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100375 continue;
376 }
377
378 // Get the dynamic backend factory function
379 BackendRegistry::FactoryFunction dynamicBackendFactoryFunction = nullptr;
380 try
381 {
382 dynamicBackendFactoryFunction = dynamicBackend->GetFactoryFunction();
383 }
384 catch (const RuntimeException& e)
385 {
Derek Lamberti08446972019-11-26 16:38:31 +0000386 ARMNN_LOG(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
387 << "\": an error has occurred when getting the backend factory function: "
388 << e.what();
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100389 continue;
390 }
391 if (dynamicBackendFactoryFunction == nullptr)
392 {
Derek Lamberti08446972019-11-26 16:38:31 +0000393 ARMNN_LOG(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
394 << "\": invalid backend factory function";
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100395 continue;
396 }
397
398 // Register the dynamic backend
Matteo Martincigh89533902019-08-15 12:08:06 +0100399 try
400 {
401 backendRegistry.Register(dynamicBackendId, dynamicBackendFactoryFunction);
402 }
403 catch (const InvalidArgumentException& e)
404 {
Derek Lamberti08446972019-11-26 16:38:31 +0000405 ARMNN_LOG(warning) << "An error has occurred when registering the dynamic backend \""
406 << dynamicBackendId << "\": " << e.what();
Matteo Martincigh89533902019-08-15 12:08:06 +0100407 continue;
408 }
409
410 // Add the id of the dynamic backend just registered to the list of registered backend ids
411 registeredBackendIds.insert(dynamicBackendId);
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100412 }
Matteo Martincigh89533902019-08-15 12:08:06 +0100413
414 return registeredBackendIds;
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100415}
416
Matteo Martincighd73cecb2019-07-24 09:15:00 +0100417} // namespace armnn