blob: fc4336f4ac6bdff31d153711b16a1d8f32bd23b5 [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{
19 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;
31}
32
33void DynamicBackendUtils::CloseHandle(const void* sharedObjectHandle)
34{
35 if (!sharedObjectHandle)
36 {
37 return;
38 }
39
40 dlclose(const_cast<void*>(sharedObjectHandle));
41}
42
Matteo Martincighac60d282019-07-25 15:25:44 +010043bool DynamicBackendUtils::IsBackendCompatible(const BackendVersion &backendVersion)
44{
45 BackendVersion backendApiVersion = IBackendInternal::GetApiVersion();
46
47 return IsBackendCompatibleImpl(backendApiVersion, backendVersion);
48}
49
50bool DynamicBackendUtils::IsBackendCompatibleImpl(const BackendVersion &backendApiVersion,
51 const BackendVersion &backendVersion)
52{
53 return backendVersion.m_Major == backendApiVersion.m_Major &&
54 backendVersion.m_Minor <= backendApiVersion.m_Minor;
55}
56
Matteo Martincighd73cecb2019-07-24 09:15:00 +010057std::string DynamicBackendUtils::GetDlError()
58{
59 const char* errorMessage = dlerror();
60 if (!errorMessage)
61 {
62 return "";
63 }
64
65 return std::string(errorMessage);
66}
67
Matteo Martincighe7d44982019-08-05 12:16:47 +010068std::vector<std::string> DynamicBackendUtils::GetBackendPaths(const std::string& overrideBackendPath)
69{
70 // Check if a path where to dynamically load the backends from is given
71 if (!overrideBackendPath.empty())
72 {
73 if (!IsPathValid(overrideBackendPath))
74 {
75 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given override path for dynamic backends \""
76 << overrideBackendPath << "\" is not valid";
77
78 return {};
79 }
80
81 return std::vector<std::string>{ overrideBackendPath };
82 }
83
84 // Expects a colon-separated list: DYNAMIC_BACKEND_PATHS="PATH_1:PATH_2:...:PATH_N"
85 const std::string backendPaths = DYNAMIC_BACKEND_PATHS;
86
87 return GetBackendPathsImpl(backendPaths);
88}
89
90std::vector<std::string> DynamicBackendUtils::GetBackendPathsImpl(const std::string& backendPaths)
91{
Matteo Martincighf97a5de2019-08-12 14:07:59 +010092 // Check if there's any path to process at all
93 if (backendPaths.empty())
94 {
95 // Silently return without issuing a warning as no paths have been passed, so
96 // the whole dynamic backend loading feature can be considered as disabled
97 return {};
98 }
99
Matteo Martincighe7d44982019-08-05 12:16:47 +0100100 std::unordered_set<std::string> uniqueBackendPaths;
101 std::vector<std::string> tempBackendPaths;
102 std::vector<std::string> validBackendPaths;
103
104 // Split the given list of paths
105 boost::split(tempBackendPaths, backendPaths, boost::is_any_of(":"));
106
107 for (const std::string& path : tempBackendPaths)
108 {
109 // Check whether the path is valid
110 if (!IsPathValid(path))
111 {
112 continue;
113 }
114
115 // Check whether the path is a duplicate
116 auto it = uniqueBackendPaths.find(path);
117 if (it != uniqueBackendPaths.end())
118 {
119 // The path is a duplicate
120 continue;
121 }
122
123 // Add the path to the set of unique paths
124 uniqueBackendPaths.insert(path);
125
126 // Add the path to the list of valid paths
127 validBackendPaths.push_back(path);
128 }
129
130 return validBackendPaths;
131}
132
133bool DynamicBackendUtils::IsPathValid(const std::string& path)
134{
135 if (path.empty())
136 {
137 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path is empty";
138 return false;
139 }
140
141 boost::filesystem::path boostPath(path);
142
143 if (!boost::filesystem::exists(boostPath))
144 {
145 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" does not exist";
146 return false;
147 }
148
149 if (!boost::filesystem::is_directory(boostPath))
150 {
151 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" is not a directory";
152 return false;
153 }
154
155 if (!boostPath.is_absolute())
156 {
157 BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" is not absolute";
158 return false;
159 }
160
161 return true;
162}
163
Jan Eilers4a539fc2019-07-25 17:08:37 +0100164std::vector<std::string> DynamicBackendUtils::GetSharedObjects(const std::vector<std::string>& backendPaths)
165{
166 std::unordered_set<std::string> uniqueSharedObjects;
167 std::vector<std::string> sharedObjects;
168
169 for (const std::string& backendPath : backendPaths)
170 {
171 using namespace boost::filesystem;
172
173 // Check if the path is valid. In case of error, IsValidPath will log an error message
174 if (!IsPathValid(backendPath))
175 {
176 continue;
177 }
178
Matteo Martincighe54aa062019-08-05 14:12:11 +0100179 // Get all the files in the current path in alphabetical order
180 std::vector<path> backendPathFiles;
181 std::copy(directory_iterator(backendPath), directory_iterator(), std::back_inserter(backendPathFiles));
182 std::sort(backendPathFiles.begin(), backendPathFiles.end());
183
Jan Eilers4a539fc2019-07-25 17:08:37 +0100184 // Go through all the files in the current backend path
Matteo Martincighe54aa062019-08-05 14:12:11 +0100185 for (const path& backendPathFile : backendPathFiles)
Jan Eilers4a539fc2019-07-25 17:08:37 +0100186 {
Matteo Martincighe54aa062019-08-05 14:12:11 +0100187 // Get only the name of the file (without the full path)
188 std::string filename = backendPathFile.filename().string();
Jan Eilers4a539fc2019-07-25 17:08:37 +0100189
190 if (filename.empty())
191 {
192 // Empty filename
193 continue;
194 }
195
196 path canonicalPath;
197 try
198 {
199 // Get the canonical path for the current file, it will throw if for example the file is a
200 // symlink that cannot be resolved
Matteo Martincighe54aa062019-08-05 14:12:11 +0100201 canonicalPath = canonical(backendPathFile);
Jan Eilers4a539fc2019-07-25 17:08:37 +0100202 }
203 catch (const filesystem_error& e)
204 {
205 BOOST_LOG_TRIVIAL(warning) << "GetSharedObjects warning: " << e.what();
206 }
207 if (canonicalPath.empty())
208 {
209 // No such file or perhaps a symlink that couldn't be resolved
210 continue;
211 }
212
213 // Check if the current filename matches the expected naming convention
214 // The expected format is: <vendor>_<name>_backend.so[<version>]
215 // e.g. "Arm_GpuAcc_backend.so" or "Arm_GpuAcc_backend.so.1.2"
216 const std::regex dynamicBackendRegex("^[a-zA-Z0-9]+_[a-zA-Z0-9]+_backend.so(\\.[0-9]+)*$");
217
218 bool filenameMatch = false;
219 try
220 {
221 // Match the filename to the expected naming scheme
222 filenameMatch = std::regex_match(filename, dynamicBackendRegex);
223 }
224 catch (const std::exception& e)
225 {
226 BOOST_LOG_TRIVIAL(warning) << "GetSharedObjects warning: " << e.what();
227 }
228 if (!filenameMatch)
229 {
230 // Filename does not match the expected naming scheme (or an error has occurred)
231 continue;
232 }
233
234 // Append the valid canonical path to the output list only if it's not a duplicate
235 std::string validCanonicalPath = canonicalPath.string();
236 auto it = uniqueSharedObjects.find(validCanonicalPath);
237 if (it == uniqueSharedObjects.end())
238 {
239 // Not a duplicate, append the canonical path to the output list
240 sharedObjects.push_back(validCanonicalPath);
241
242 // Add the canonical path to the collection of unique shared objects
243 uniqueSharedObjects.insert(validCanonicalPath);
244 }
245 }
246 }
247
248 return sharedObjects;
249}
250
Matteo Martincighe54aa062019-08-05 14:12:11 +0100251std::vector<DynamicBackendPtr> DynamicBackendUtils::CreateDynamicBackends(const std::vector<std::string>& sharedObjects)
252{
253 // Create a list of dynamic backends
254 std::vector<DynamicBackendPtr> dynamicBackends;
255 for (const std::string& sharedObject : sharedObjects)
256 {
257 // Create a handle to the shared object
258 void* sharedObjectHandle = nullptr;
259 try
260 {
261 sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObject);
262 }
263 catch (const RuntimeException& e)
264 {
265 BOOST_LOG_TRIVIAL(warning) << "Cannot create a handle to the shared object file \""
266 << sharedObject << "\": " << e.what();
267 continue;
268 }
269 if (!sharedObjectHandle)
270 {
271 BOOST_LOG_TRIVIAL(warning) << "Invalid handle to the shared object file \"" << sharedObject << "\"";
272
273 continue;
274 }
275
276 // Create a dynamic backend object
277 DynamicBackendPtr dynamicBackend;
278 try
279 {
280 dynamicBackend.reset(new DynamicBackend(sharedObjectHandle));
281 }
282 catch (const Exception& e)
283 {
284 BOOST_LOG_TRIVIAL(warning) << "Cannot create a valid dynamic backend from the shared object file \""
285 << sharedObject << "\": " << e.what();
286 continue;
287 }
288 if (!dynamicBackend)
289 {
290 BOOST_LOG_TRIVIAL(warning) << "Invalid dynamic backend object for the shared object file \""
291 << sharedObject << "\"";
Matteo Martincighe54aa062019-08-05 14:12:11 +0100292 continue;
293 }
294
295 // Append the newly created dynamic backend to the list
296 dynamicBackends.push_back(std::move(dynamicBackend));
297 }
298
299 return dynamicBackends;
300}
301
Matteo Martincigh89533902019-08-15 12:08:06 +0100302BackendIdSet DynamicBackendUtils::RegisterDynamicBackends(const std::vector<DynamicBackendPtr>& dynamicBackends)
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100303{
304 // Get a reference of the backend registry
305 BackendRegistry& backendRegistry = BackendRegistryInstance();
306
Matteo Martincigh89533902019-08-15 12:08:06 +0100307 // Register the dynamic backends in the backend registry, and return a list of registered backend ids
308 return RegisterDynamicBackendsImpl(backendRegistry, dynamicBackends);
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100309}
310
Matteo Martincigh89533902019-08-15 12:08:06 +0100311BackendIdSet DynamicBackendUtils::RegisterDynamicBackendsImpl(BackendRegistry& backendRegistry,
312 const std::vector<DynamicBackendPtr>& dynamicBackends)
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100313{
Matteo Martincigh89533902019-08-15 12:08:06 +0100314 // Initialize the list of registered backend ids
315 BackendIdSet registeredBackendIds;
316
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100317 // Register the dynamic backends in the backend registry
318 for (const DynamicBackendPtr& dynamicBackend : dynamicBackends)
319 {
Matteo Martincigh89533902019-08-15 12:08:06 +0100320 // Get the id of the dynamic backend
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100321 BackendId dynamicBackendId;
322 try
323 {
324 dynamicBackendId = dynamicBackend->GetBackendId();
325 }
326 catch (const RuntimeException& e)
327 {
328 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend, "
329 << "an error has occurred when getting the backend id: " << e.what();
330 continue;
331 }
332 if (dynamicBackendId.IsEmpty() ||
333 dynamicBackendId.IsUndefined())
334 {
335 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend, invalid backend id: " << dynamicBackendId;
336 continue;
337 }
338
339 // Check whether the dynamic backend is already registered
340 bool backendAlreadyRegistered = backendRegistry.IsBackendRegistered(dynamicBackendId);
341 if (backendAlreadyRegistered)
342 {
343 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
344 << "\": backend already registered";
345 continue;
346 }
347
348 // Get the dynamic backend factory function
349 BackendRegistry::FactoryFunction dynamicBackendFactoryFunction = nullptr;
350 try
351 {
352 dynamicBackendFactoryFunction = dynamicBackend->GetFactoryFunction();
353 }
354 catch (const RuntimeException& e)
355 {
356 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
357 << "\": an error has occurred when getting the backend factory function: "
358 << e.what();
359 continue;
360 }
361 if (dynamicBackendFactoryFunction == nullptr)
362 {
363 BOOST_LOG_TRIVIAL(warning) << "Cannot register dynamic backend \"" << dynamicBackendId
364 << "\": invalid backend factory function";
365 continue;
366 }
367
368 // Register the dynamic backend
Matteo Martincigh89533902019-08-15 12:08:06 +0100369 try
370 {
371 backendRegistry.Register(dynamicBackendId, dynamicBackendFactoryFunction);
372 }
373 catch (const InvalidArgumentException& e)
374 {
375 BOOST_LOG_TRIVIAL(warning) << "An error has occurred when registering the dynamic backend \""
376 << dynamicBackendId << "\": " << e.what();
377 continue;
378 }
379
380 // Add the id of the dynamic backend just registered to the list of registered backend ids
381 registeredBackendIds.insert(dynamicBackendId);
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100382 }
Matteo Martincigh89533902019-08-15 12:08:06 +0100383
384 return registeredBackendIds;
Matteo Martincigh0c2b2892019-08-05 14:12:11 +0100385}
386
Matteo Martincighd73cecb2019-07-24 09:15:00 +0100387} // namespace armnn