IVGCVSW-3599 Create the GetSharedObjects method in DynamicBackendUtils
authorJan Eilers <jan.eilers@arm.com>
Thu, 25 Jul 2019 16:08:37 +0000 (17:08 +0100)
committerMatteo Martincigh <matteo.martincigh@arm.com>
Mon, 5 Aug 2019 16:32:25 +0000 (16:32 +0000)
 * Added GetSharedObjects to DynamicBackendUtils
 * Goes through the given paths and returns a list of files that
   match the naming scheme for dynamic backends
 * Added unit test
 * Update the cmake file to generate the test files/symlinks

Signed-off-by: Matteo Martincigh <matteo.martincigh@arm.com>
Change-Id: Ibfc0bad740b72696714d343eb32a593c2ec1f8c6

src/backends/backendsCommon/DynamicBackendUtils.cpp
src/backends/backendsCommon/DynamicBackendUtils.hpp
src/backends/backendsCommon/test/DynamicBackendTests.cpp
src/backends/backendsCommon/test/DynamicBackendTests.hpp

index ae36a24..1dea802 100644 (file)
@@ -5,10 +5,12 @@
 
 #include "DynamicBackendUtils.hpp"
 
-#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/log/trivial.hpp>
 
+#include <regex>
+
 namespace armnn
 {
 
@@ -151,4 +153,86 @@ bool DynamicBackendUtils::IsPathValid(const std::string& path)
     return true;
 }
 
+std::vector<std::string> DynamicBackendUtils::GetSharedObjects(const std::vector<std::string>& backendPaths)
+{
+    std::unordered_set<std::string> uniqueSharedObjects;
+    std::vector<std::string> sharedObjects;
+
+    for (const std::string& backendPath : backendPaths)
+    {
+        using namespace boost::filesystem;
+
+        // Check if the path is valid. In case of error, IsValidPath will log an error message
+        if (!IsPathValid(backendPath))
+        {
+            continue;
+        }
+
+        // Go through all the files in the current backend path
+        for (directory_iterator fileIterator(backendPath); fileIterator != directory_iterator(); fileIterator++)
+        {
+            path filePath = *fileIterator;
+            std::string filename = filePath.filename().string();
+
+            if (filename.empty())
+            {
+                // Empty filename
+                continue;
+            }
+
+            path canonicalPath;
+            try
+            {
+                // Get the canonical path for the current file, it will throw if for example the file is a
+                // symlink that cannot be resolved
+                canonicalPath = canonical(filePath);
+            }
+            catch (const filesystem_error& e)
+            {
+                BOOST_LOG_TRIVIAL(warning) << "GetSharedObjects warning: " << e.what();
+            }
+            if (canonicalPath.empty())
+            {
+                // No such file or perhaps a symlink that couldn't be resolved
+                continue;
+            }
+
+            // Check if the current filename matches the expected naming convention
+            // The expected format is: <vendor>_<name>_backend.so[<version>]
+            // e.g. "Arm_GpuAcc_backend.so" or "Arm_GpuAcc_backend.so.1.2"
+            const std::regex dynamicBackendRegex("^[a-zA-Z0-9]+_[a-zA-Z0-9]+_backend.so(\\.[0-9]+)*$");
+
+            bool filenameMatch = false;
+            try
+            {
+                // Match the filename to the expected naming scheme
+                filenameMatch = std::regex_match(filename, dynamicBackendRegex);
+            }
+            catch (const std::exception& e)
+            {
+                BOOST_LOG_TRIVIAL(warning) << "GetSharedObjects warning: " << e.what();
+            }
+            if (!filenameMatch)
+            {
+                // Filename does not match the expected naming scheme (or an error has occurred)
+                continue;
+            }
+
+            // Append the valid canonical path to the output list only if it's not a duplicate
+            std::string validCanonicalPath = canonicalPath.string();
+            auto it = uniqueSharedObjects.find(validCanonicalPath);
+            if (it == uniqueSharedObjects.end())
+            {
+                // Not a duplicate, append the canonical path to the output list
+                sharedObjects.push_back(validCanonicalPath);
+
+                // Add the canonical path to the collection of unique shared objects
+                uniqueSharedObjects.insert(validCanonicalPath);
+            }
+        }
+    }
+
+    return sharedObjects;
+}
+
 } // namespace armnn
index f0bfd3b..b327a9e 100644 (file)
@@ -35,6 +35,7 @@ public:
 
     static std::vector<std::string> GetBackendPaths(const std::string& overrideBackendPath = "");
     static bool IsPathValid(const std::string& path);
+    static std::vector<std::string> GetSharedObjects(const std::vector<std::string>& backendPaths);
 
 protected:
     /// Protected methods for testing purposes
index 16d07c1..a467b0e 100644 (file)
@@ -43,4 +43,6 @@ ARMNN_SIMPLE_TEST_CASE(CreateDynamicBackendObjectInvalidInterface7,
 ARMNN_SIMPLE_TEST_CASE(GetBackendPaths, GetBackendPathsTestImpl)
 ARMNN_SIMPLE_TEST_CASE(GetBackendPathsOverride, GetBackendPathsOverrideTestImpl)
 
+ARMNN_SIMPLE_TEST_CASE(GetSharedObjects, GetSharedObjectsTestImpl);
+
 BOOST_AUTO_TEST_SUITE_END()
index 3277811..b4af705 100644 (file)
@@ -10,7 +10,6 @@
 
 #include <string>
 #include <memory>
-
 #include <string>
 
 #include <boost/test/unit_test.hpp>
@@ -570,3 +569,84 @@ void GetBackendPathsOverrideTestImpl()
     std::vector<std::string> invalidResult = DynamicBackendUtils::GetBackendPaths(subDir4);
     BOOST_TEST(invalidResult.empty());
 }
+
+void GetSharedObjectsTestImpl()
+{
+    using namespace armnn;
+    using namespace boost::filesystem;
+
+    //
+    // The test sub-directory backendsTestPath1/ contains the following test files:
+    //
+    // Arm_GpuAcc_backend.so                                       -> valid (basic backend name)
+    // Arm_GpuAcc_backend.so.1                                     -> valid (single field version number)
+    // Arm_GpuAcc_backend.so.1.2                                   -> valid (multiple field version number)
+    // Arm_GpuAcc_backend.so.1.2.3                                 -> valid (multiple field version number)
+    // Arm_GpuAcc_backend.so.10.1.27                               -> valid (Multiple digit version)
+    // Arm_GpuAcc_backend.so.10.1.33.                              -> not valid (dot not followed by version number)
+    // Arm_GpuAcc_backend.so.3.4..5                                -> not valid (dot not followed by version number)
+    // Arm_GpuAcc_backend.so.1,1.1                                 -> not valid (comma instead of dot in the version)
+    //
+    // Arm123_GpuAcc_backend.so                                    -> valid (digits in vendor name are allowed)
+    // Arm_GpuAcc456_backend.so                                    -> valid (digits in backend id are allowed)
+    // Arm%Co_GpuAcc_backend.so                                    -> not valid (invalid character in vendor name)
+    // Arm_Gpu.Acc_backend.so                                      -> not valid (invalid character in backend id)
+    //
+    // GpuAcc_backend.so                                           -> not valid (missing vendor name)
+    // _GpuAcc_backend.so                                          -> not valid (missing vendor name)
+    // Arm__backend.so                                             -> not valid (missing backend id)
+    // Arm_GpuAcc.so                                               -> not valid (missing "backend" at the end)
+    // __backend.so                                                -> not valid (missing vendor name and backend id)
+    // __.so                                                       -> not valid (missing all fields)
+    //
+    // Arm_GpuAcc_backend                                          -> not valid (missing at least ".so" at the end)
+    // Arm_GpuAcc_backend_v1.2.so                                  -> not valid (extra version info at the end)
+    //
+    // The test sub-directory backendsTestPath1/ contains the following test files:
+    //
+    // Arm_CpuAcc_backend.so                                       -> valid (basic backend name)
+    // Arm_CpuAcc_backend.so.1 -> Arm_CpuAcc_backend.so            -> valid (symlink to valid backend file)
+    // Arm_CpuAcc_backend.so.1.2 -> Arm_CpuAcc_backend.so.1        -> valid (symlink to valid symlink)
+    // Arm_CpuAcc_backend.so.1.2.3 -> Arm_CpuAcc_backend.so.1.2    -> valid (symlink to valid symlink)
+    //
+    // Arm_no_backend.so -> nothing                                -> not valid (symlink resolves to non-existent file)
+    //
+    // Arm_GpuAcc_backend.so                                       -> valid (but duplicated from backendsTestPath1/)
+
+    std::string testDynamicBackendsSubDir1 = GetTestSubDirectory(g_TestDynamicBackendsFileParsingSubDir1);
+    std::string testDynamicBackendsSubDir2 = GetTestSubDirectory(g_TestDynamicBackendsFileParsingSubDir2);
+    std::string testDynamicBackendsSubDir3 = GetTestSubDirectory(g_TestDynamicBackendsFileParsingSubDir3);
+    std::string testDynamicBackendsSubDir4 = GetTestSubDirectory(g_TestDynamicBackendsFileParsingSubDir4);
+    BOOST_CHECK(exists(testDynamicBackendsSubDir1));
+    BOOST_CHECK(exists(testDynamicBackendsSubDir2));
+    BOOST_CHECK(exists(testDynamicBackendsSubDir3));
+    BOOST_CHECK(!exists(testDynamicBackendsSubDir4));
+
+    std::vector<std::string> backendPaths
+    {
+        testDynamicBackendsSubDir1,
+        testDynamicBackendsSubDir2,
+        testDynamicBackendsSubDir3,
+        testDynamicBackendsSubDir4
+    };
+    std::vector<std::string> sharedObjects = DynamicBackendUtils::GetSharedObjects(backendPaths);
+    std::unordered_set<std::string> expectedSharedObjects
+    {
+        testDynamicBackendsSubDir1 + "Arm_GpuAcc_backend.so",         // Basic backend name
+        testDynamicBackendsSubDir1 + "Arm_GpuAcc_backend.so.1",       // Single field version number
+        testDynamicBackendsSubDir1 + "Arm_GpuAcc_backend.so.1.2",     // Multiple field version number
+        testDynamicBackendsSubDir1 + "Arm_GpuAcc_backend.so.1.2.3",   // Multiple field version number
+        testDynamicBackendsSubDir1 + "Arm_GpuAcc_backend.so.10.1.27", // Multiple digit version
+        testDynamicBackendsSubDir1 + "Arm123_GpuAcc_backend.so",      // Digits in vendor name are allowed
+        testDynamicBackendsSubDir1 + "Arm_GpuAcc456_backend.so",      // Digits in backend id are allowed
+        testDynamicBackendsSubDir2 + "Arm_CpuAcc_backend.so",         // Duplicate symlinks removed
+        testDynamicBackendsSubDir2 + "Arm_GpuAcc_backend.so"          // Duplicates on different paths are allowed
+    };
+
+    BOOST_TEST(sharedObjects.size() == expectedSharedObjects.size());
+    for (const std::string& sharedObject : sharedObjects)
+    {
+        auto it = expectedSharedObjects.find(sharedObject);
+        BOOST_TEST((it != expectedSharedObjects.end()));
+    }
+}