IVGCVSW-3599 Create the GetSharedObjects method in DynamicBackendUtils
[platform/upstream/armnn.git] / src / backends / backendsCommon / DynamicBackendUtils.cpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include "DynamicBackendUtils.hpp"
7
8 #include <boost/filesystem.hpp>
9 #include <boost/algorithm/string.hpp>
10 #include <boost/log/trivial.hpp>
11
12 #include <regex>
13
14 namespace armnn
15 {
16
17 void* 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
24     void* sharedObjectHandle = dlopen(sharedObjectPath.c_str(), RTLD_LAZY | RTLD_GLOBAL);
25     if (!sharedObjectHandle)
26     {
27         throw RuntimeException(boost::str(boost::format("OpenHandle error: %1%") % GetDlError()));
28     }
29
30     return sharedObjectHandle;
31 }
32
33 void DynamicBackendUtils::CloseHandle(const void* sharedObjectHandle)
34 {
35     if (!sharedObjectHandle)
36     {
37         return;
38     }
39
40     dlclose(const_cast<void*>(sharedObjectHandle));
41 }
42
43 bool DynamicBackendUtils::IsBackendCompatible(const BackendVersion &backendVersion)
44 {
45     BackendVersion backendApiVersion = IBackendInternal::GetApiVersion();
46
47     return IsBackendCompatibleImpl(backendApiVersion, backendVersion);
48 }
49
50 bool 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
57 std::string DynamicBackendUtils::GetDlError()
58 {
59     const char* errorMessage = dlerror();
60     if (!errorMessage)
61     {
62         return "";
63     }
64
65     return std::string(errorMessage);
66 }
67
68 std::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
90 std::vector<std::string> DynamicBackendUtils::GetBackendPathsImpl(const std::string& backendPaths)
91 {
92     std::unordered_set<std::string> uniqueBackendPaths;
93     std::vector<std::string> tempBackendPaths;
94     std::vector<std::string> validBackendPaths;
95
96     // Split the given list of paths
97     boost::split(tempBackendPaths, backendPaths, boost::is_any_of(":"));
98
99     for (const std::string& path : tempBackendPaths)
100     {
101         // Check whether the path is valid
102         if (!IsPathValid(path))
103         {
104             continue;
105         }
106
107         // Check whether the path is a duplicate
108         auto it = uniqueBackendPaths.find(path);
109         if (it != uniqueBackendPaths.end())
110         {
111             // The path is a duplicate
112             continue;
113         }
114
115         // Add the path to the set of unique paths
116         uniqueBackendPaths.insert(path);
117
118         // Add the path to the list of valid paths
119         validBackendPaths.push_back(path);
120     }
121
122     return validBackendPaths;
123 }
124
125 bool DynamicBackendUtils::IsPathValid(const std::string& path)
126 {
127     if (path.empty())
128     {
129         BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path is empty";
130         return false;
131     }
132
133     boost::filesystem::path boostPath(path);
134
135     if (!boost::filesystem::exists(boostPath))
136     {
137         BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" does not exist";
138         return false;
139     }
140
141     if (!boost::filesystem::is_directory(boostPath))
142     {
143         BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" is not a directory";
144         return false;
145     }
146
147     if (!boostPath.is_absolute())
148     {
149         BOOST_LOG_TRIVIAL(warning) << "WARNING: The given backend path \"" << path << "\" is not absolute";
150         return false;
151     }
152
153     return true;
154 }
155
156 std::vector<std::string> DynamicBackendUtils::GetSharedObjects(const std::vector<std::string>& backendPaths)
157 {
158     std::unordered_set<std::string> uniqueSharedObjects;
159     std::vector<std::string> sharedObjects;
160
161     for (const std::string& backendPath : backendPaths)
162     {
163         using namespace boost::filesystem;
164
165         // Check if the path is valid. In case of error, IsValidPath will log an error message
166         if (!IsPathValid(backendPath))
167         {
168             continue;
169         }
170
171         // Go through all the files in the current backend path
172         for (directory_iterator fileIterator(backendPath); fileIterator != directory_iterator(); fileIterator++)
173         {
174             path filePath = *fileIterator;
175             std::string filename = filePath.filename().string();
176
177             if (filename.empty())
178             {
179                 // Empty filename
180                 continue;
181             }
182
183             path canonicalPath;
184             try
185             {
186                 // Get the canonical path for the current file, it will throw if for example the file is a
187                 // symlink that cannot be resolved
188                 canonicalPath = canonical(filePath);
189             }
190             catch (const filesystem_error& e)
191             {
192                 BOOST_LOG_TRIVIAL(warning) << "GetSharedObjects warning: " << e.what();
193             }
194             if (canonicalPath.empty())
195             {
196                 // No such file or perhaps a symlink that couldn't be resolved
197                 continue;
198             }
199
200             // Check if the current filename matches the expected naming convention
201             // The expected format is: <vendor>_<name>_backend.so[<version>]
202             // e.g. "Arm_GpuAcc_backend.so" or "Arm_GpuAcc_backend.so.1.2"
203             const std::regex dynamicBackendRegex("^[a-zA-Z0-9]+_[a-zA-Z0-9]+_backend.so(\\.[0-9]+)*$");
204
205             bool filenameMatch = false;
206             try
207             {
208                 // Match the filename to the expected naming scheme
209                 filenameMatch = std::regex_match(filename, dynamicBackendRegex);
210             }
211             catch (const std::exception& e)
212             {
213                 BOOST_LOG_TRIVIAL(warning) << "GetSharedObjects warning: " << e.what();
214             }
215             if (!filenameMatch)
216             {
217                 // Filename does not match the expected naming scheme (or an error has occurred)
218                 continue;
219             }
220
221             // Append the valid canonical path to the output list only if it's not a duplicate
222             std::string validCanonicalPath = canonicalPath.string();
223             auto it = uniqueSharedObjects.find(validCanonicalPath);
224             if (it == uniqueSharedObjects.end())
225             {
226                 // Not a duplicate, append the canonical path to the output list
227                 sharedObjects.push_back(validCanonicalPath);
228
229                 // Add the canonical path to the collection of unique shared objects
230                 uniqueSharedObjects.insert(validCanonicalPath);
231             }
232         }
233     }
234
235     return sharedObjects;
236 }
237
238 } // namespace armnn