blob: f893458b885c90f3ef1b5c554ec38f1917968f0b [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
Matteo Martincighe5b8eb92019-11-28 15:45:42 +00006#include <backendsCommon/DynamicBackendUtils.hpp>
Matteo Martincighd73cecb2019-07-24 09:15:00 +01007
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>
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{
Rob Hughes91e1d892019-08-23 10:11:58 +010018#if defined(__unix__)
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 {
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;
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{
Rob Hughes91e1d892019-08-23 10:11:58 +010038#if defined(__unix__)
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{
Rob Hughes91e1d892019-08-23 10:11:58 +010066#if defined(__unix__)
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;
112 std::vector<std::string> tempBackendPaths;
113 std::vector<std::string> validBackendPaths;
114
115 // Split the given list of paths
116 boost::split(tempBackendPaths, backendPaths, boost::is_any_of(":"));
117
118 for (const std::string& path : tempBackendPaths)
119 {
120 // Check whether the path is valid
121 if (!IsPathValid(path))
122 {
123 continue;
124 }
125
126 // Check whether the path is a duplicate
127 auto it = uniqueBackendPaths.find(path);
128 if (it != uniqueBackendPaths.end())
129 {
130 // The path is a duplicate
131 continue;
132 }
133
134 // Add the path to the set of unique paths
135 uniqueBackendPaths.insert(path);
136
137 // Add the path to the list of valid paths
138 validBackendPaths.push_back(path);
139 }
140
141 return validBackendPaths;
142}
143
144bool DynamicBackendUtils::IsPathValid(const std::string& path)
145{
146 if (path.empty())
147 {
Derek Lamberti08446972019-11-26 16:38:31 +0000148 ARMNN_LOG(warning) << "WARNING: The given backend path is empty";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100149 return false;
150 }
151
152 boost::filesystem::path boostPath(path);
153
154 if (!boost::filesystem::exists(boostPath))
155 {
Derek Lamberti08446972019-11-26 16:38:31 +0000156 ARMNN_LOG(warning) << "WARNING: The given backend path \"" << path << "\" does not exist";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100157 return false;
158 }
159
160 if (!boost::filesystem::is_directory(boostPath))
161 {
Derek Lamberti08446972019-11-26 16:38:31 +0000162 ARMNN_LOG(warning) << "WARNING: The given backend path \"" << path << "\" is not a directory";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100163 return false;
164 }
165
166 if (!boostPath.is_absolute())
167 {
Derek Lamberti08446972019-11-26 16:38:31 +0000168 ARMNN_LOG(warning) << "WARNING: The given backend path \"" << path << "\" is not absolute";
Matteo Martincighe7d44982019-08-05 12:16:47 +0100169 return false;
170 }
171
172 return true;
173}
174
Jan Eilers4a539fc2019-07-25 17:08:37 +0100175std::vector<std::string> DynamicBackendUtils::GetSharedObjects(const std::vector<std::string>& backendPaths)
176{
177 std::unordered_set<std::string> uniqueSharedObjects;
178 std::vector<std::string> sharedObjects;
179
180 for (const std::string& backendPath : backendPaths)
181 {
182 using namespace boost::filesystem;
183
184 // Check if the path is valid. In case of error, IsValidPath will log an error message
185 if (!IsPathValid(backendPath))
186 {
187 continue;
188 }
189
Matteo Martincighe54aa062019-08-05 14:12:11 +0100190 // Get all the files in the current path in alphabetical order
191 std::vector<path> backendPathFiles;
192 std::copy(directory_iterator(backendPath), directory_iterator(), std::back_inserter(backendPathFiles));
193 std::sort(backendPathFiles.begin(), backendPathFiles.end());
194
Jan Eilers4a539fc2019-07-25 17:08:37 +0100195 // Go through all the files in the current backend path
Matteo Martincighe54aa062019-08-05 14:12:11 +0100196 for (const path& backendPathFile : backendPathFiles)
Jan Eilers4a539fc2019-07-25 17:08:37 +0100197 {
Matteo Martincighe54aa062019-08-05 14:12:11 +0100198 // Get only the name of the file (without the full path)
199 std::string filename = backendPathFile.filename().string();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100200
201 if (filename.empty())
202 {
203 // Empty filename
204 continue;
205 }
206
207 path canonicalPath;
208 try
209 {
210 // Get the canonical path for the current file, it will throw if for example the file is a
211 // symlink that cannot be resolved
Matteo Martincighe54aa062019-08-05 14:12:11 +0100212 canonicalPath = canonical(backendPathFile);
Jan Eilers4a539fc2019-07-25 17:08:37 +0100213 }
214 catch (const filesystem_error& e)
215 {
Derek Lamberti08446972019-11-26 16:38:31 +0000216 ARMNN_LOG(warning) << "GetSharedObjects warning: " << e.what();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100217 }
218 if (canonicalPath.empty())
219 {
220 // No such file or perhaps a symlink that couldn't be resolved
221 continue;
222 }
223
224 // Check if the current filename matches the expected naming convention
225 // The expected format is: <vendor>_<name>_backend.so[<version>]
226 // e.g. "Arm_GpuAcc_backend.so" or "Arm_GpuAcc_backend.so.1.2"
227 const std::regex dynamicBackendRegex("^[a-zA-Z0-9]+_[a-zA-Z0-9]+_backend.so(\\.[0-9]+)*$");
228
229 bool filenameMatch = false;
230 try
231 {
232 // Match the filename to the expected naming scheme
233 filenameMatch = std::regex_match(filename, dynamicBackendRegex);
234 }
235 catch (const std::exception& e)
236 {
Derek Lamberti08446972019-11-26 16:38:31 +0000237 ARMNN_LOG(warning) << "GetSharedObjects warning: " << e.what();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100238 }
239 if (!filenameMatch)
240 {
241 // Filename does not match the expected naming scheme (or an error has occurred)
242 continue;
243 }
244
245 // Append the valid canonical path to the output list only if it's not a duplicate
246 std::string validCanonicalPath = canonicalPath.string();
247 auto it = uniqueSharedObjects.find(validCanonicalPath);
248 if (it == uniqueSharedObjects.end())
249 {
250 // Not a duplicate, append the canonical path to the output list
251 sharedObjects.push_back(validCanonicalPath);
252
253 // Add the canonical path to the collection of unique shared objects
254 uniqueSharedObjects.insert(validCanonicalPath);
255 }
256 }
257 }
258
259 return sharedObjects;
260}
261
Matteo Martincighe54aa062019-08-05 14:12:11 +0100262std::vector<DynamicBackendPtr> DynamicBackendUtils::CreateDynamicBackends(const std::vector<std::string>& sharedObjects)
263{
264 // Create a list of dynamic backends
265 std::vector<DynamicBackendPtr> dynamicBackends;
266 for (const std::string& sharedObject : sharedObjects)
267 {
268 // Create a handle to the shared object
269 void* sharedObjectHandle = nullptr;
270 try
271 {
272 sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObject);
273 }
274 catch (const RuntimeException& e)
275 {
Derek Lamberti08446972019-11-26 16:38:31 +0000276 ARMNN_LOG(warning) << "Cannot create a handle to the shared object file \""
277 << sharedObject << "\": " << e.what();
Matteo Martincighe54aa062019-08-05 14:12:11 +0100278 continue;
279 }
280 if (!sharedObjectHandle)
281 {
Derek Lamberti08446972019-11-26 16:38:31 +0000282 ARMNN_LOG(warning) << "Invalid handle to the shared object file \"" << sharedObject << "\"";
Matteo Martincighe54aa062019-08-05 14:12:11 +0100283
284 continue;
285 }
286
287 // Create a dynamic backend object
288 DynamicBackendPtr dynamicBackend;
289 try
290 {
291 dynamicBackend.reset(new DynamicBackend(sharedObjectHandle));
292 }
293 catch (const Exception& e)
294 {
Derek Lamberti08446972019-11-26 16:38:31 +0000295 ARMNN_LOG(warning) << "Cannot create a valid dynamic backend from the shared object file \""
296 << sharedObject << "\": " << e.what();
Matteo Martincighe54aa062019-08-05 14:12:11 +0100297 continue;
298 }
299 if (!dynamicBackend)
300 {
Derek Lamberti08446972019-11-26 16:38:31 +0000301 ARMNN_LOG(warning) << "Invalid dynamic backend object for the shared object file \""
302 << sharedObject << "\"";
Matteo Martincighe54aa062019-08-05 14:12:11 +0100303 continue;
304 }
305
306 // Append the newly created dynamic backend to the list
307 dynamicBackends.push_back(std::move(dynamicBackend));
308 }
309
310 return dynamicBackends;
311}
312
Narumol Prangnawarat60a20fb2019-12-09 17:24:41 +0000313void DynamicBackendUtils::DeregisterDynamicBackends(const BackendIdSet& dynamicBackends)
314{
315 // Get a reference of the backend registry
316 BackendRegistry& backendRegistry = BackendRegistryInstance();
317
318 for (const auto& id : dynamicBackends)
319 {
320 backendRegistry.Deregister(id);
321 }
322
323}
324
Matteo Martincigh89533902019-08-15 12:08:06 +0100325BackendIdSet DynamicBackendUtils::RegisterDynamicBackends(const std::vector<DynamicBackendPtr>& dynamicBackends)
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100326{
327 // Get a reference of the backend registry
328 BackendRegistry& backendRegistry = BackendRegistryInstance();
329
Matteo Martincigh89533902019-08-15 12:08:06 +0100330 // Register the dynamic backends in the backend registry, and return a list of registered backend ids
331 return RegisterDynamicBackendsImpl(backendRegistry, dynamicBackends);
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100332}
333
Matteo Martincigh89533902019-08-15 12:08:06 +0100334BackendIdSet DynamicBackendUtils::RegisterDynamicBackendsImpl(BackendRegistry& backendRegistry,
335 const std::vector<DynamicBackendPtr>& dynamicBackends)
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100336{
Matteo Martincigh89533902019-08-15 12:08:06 +0100337 // Initialize the list of registered backend ids
338 BackendIdSet registeredBackendIds;
339
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100340 // Register the dynamic backends in the backend registry
341 for (const DynamicBackendPtr& dynamicBackend : dynamicBackends)
342 {
Matteo Martincigh89533902019-08-15 12:08:06 +0100343 // Get the id of the dynamic backend
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100344 BackendId dynamicBackendId;
345 try
346 {
347 dynamicBackendId = dynamicBackend->GetBackendId();
348 }
349 catch (const RuntimeException& e)
350 {
Derek Lamberti08446972019-11-26 16:38:31 +0000351 ARMNN_LOG(warning) << "Cannot register dynamic backend, "
352 << "an error has occurred when getting the backend id: " << e.what();
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100353 continue;
354 }
355 if (dynamicBackendId.IsEmpty() ||
356 dynamicBackendId.IsUndefined())
357 {
Derek Lamberti08446972019-11-26 16:38:31 +0000358 ARMNN_LOG(warning) << "Cannot register dynamic backend, invalid backend id: " << dynamicBackendId;
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100359 continue;
360 }
361
362 // Check whether the dynamic backend is already registered
363 bool backendAlreadyRegistered = backendRegistry.IsBackendRegistered(dynamicBackendId);
364 if (backendAlreadyRegistered)
365 {
Derek Lamberti08446972019-11-26 16:38:31 +0000366 ARMNN_LOG(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
367 << "\": backend already registered";
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100368 continue;
369 }
370
371 // Get the dynamic backend factory function
372 BackendRegistry::FactoryFunction dynamicBackendFactoryFunction = nullptr;
373 try
374 {
375 dynamicBackendFactoryFunction = dynamicBackend->GetFactoryFunction();
376 }
377 catch (const RuntimeException& e)
378 {
Derek Lamberti08446972019-11-26 16:38:31 +0000379 ARMNN_LOG(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
380 << "\": an error has occurred when getting the backend factory function: "
381 << e.what();
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100382 continue;
383 }
384 if (dynamicBackendFactoryFunction == nullptr)
385 {
Derek Lamberti08446972019-11-26 16:38:31 +0000386 ARMNN_LOG(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
387 << "\": invalid backend factory function";
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100388 continue;
389 }
390
391 // Register the dynamic backend
Matteo Martincigh89533902019-08-15 12:08:06 +0100392 try
393 {
394 backendRegistry.Register(dynamicBackendId, dynamicBackendFactoryFunction);
395 }
396 catch (const InvalidArgumentException& e)
397 {
Derek Lamberti08446972019-11-26 16:38:31 +0000398 ARMNN_LOG(warning) << "An error has occurred when registering the dynamic backend \""
399 << dynamicBackendId << "\": " << e.what();
Matteo Martincigh89533902019-08-15 12:08:06 +0100400 continue;
401 }
402
403 // Add the id of the dynamic backend just registered to the list of registered backend ids
404 registeredBackendIds.insert(dynamicBackendId);
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100405 }
Matteo Martincigh89533902019-08-15 12:08:06 +0100406
407 return registeredBackendIds;
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100408}
409
Matteo Martincighd73cecb2019-07-24 09:15:00 +0100410} // namespace armnn