IVGCVSW-7405 Improving error handling around creating directories.

The -F execute network option creates a directory to print intermediate
tensors but minor problems caused serious failures. This attempts
to clean up the error handling.

Signed-off-by: Colm Donelan <colm.donelan@arm.com>
Change-Id: Ia44c008919b1bee299b43a672235b1fcc25bf1bd
diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp
index 42388bf..e81b87b 100644
--- a/src/armnn/Network.cpp
+++ b/src/armnn/Network.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright © 2017,2022 Arm Ltd and Contributors. All rights reserved.
+// Copyright © 2017,2022,2023 Arm Ltd and Contributors. All rights reserved.
 // SPDX-License-Identifier: MIT
 //
 
@@ -1731,8 +1731,17 @@
     else if (options.m_DebugToFile)
     {
         // Setup the output file path
-        armnnUtils::Filesystem::CreateDirectory("/ArmNNIntermediateLayerOutputs");
-        Optimizer::Pass(optGraph, MakeOptimizations(InsertDebugToFileLayer()));
+        try
+        {
+            auto result = armnnUtils::Filesystem::CreateDirectory("/ArmNNIntermediateLayerOutputs");
+            ARMNN_LOG(info) << "Intermediate tensors will be written to: " << result;
+            Optimizer::Pass(optGraph, MakeOptimizations(InsertDebugToFileLayer()));
+        }
+        catch (const armnn::RuntimeException& e)
+        {
+            // If we cannot create the output directory then we'll issue a warning and continue.
+            ARMNN_LOG(warning) << "Unable to print intermediate layer outputs : " << e.what();
+        }
     }
 
     // Calculate the compatibility strategies for tensor handles
diff --git a/src/armnnUtils/Filesystem.cpp b/src/armnnUtils/Filesystem.cpp
index 1577d2d..78f928a 100644
--- a/src/armnnUtils/Filesystem.cpp
+++ b/src/armnnUtils/Filesystem.cpp
@@ -1,11 +1,11 @@
 //
-// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
+// Copyright © 2020,2023 Arm Ltd and Contributors. All rights reserved.
 // SPDX-License-Identifier: MIT
 //
 #if !defined(ARMNN_DISABLE_FILESYSTEM)
 
+#include <armnn/Exceptions.hpp>
 #include <armnnUtils/Filesystem.hpp>
-#include "armnn/Exceptions.hpp"
 
 namespace armnnUtils
 {
@@ -42,31 +42,54 @@
  *
  * @param path is the path required in the temporary directory.
  * @return path consisting of system temporary directory.
+ * @throws RuntimeException if the directory cannot be created or exists but cannot be removed.
  */
 std::string CreateDirectory(std::string path)
 {
+    // This line is very unlikely to throw an exception.
     fs::path tmpDir = fs::temp_directory_path();
-    mode_t permissions = 0733;
-    int result = 0;
-
     std::string full_path = tmpDir.generic_string() + path;
     if (fs::exists(full_path))
     {
-        fs::remove_all(full_path);
+        try
+        {
+            // This could throw an exception on a multi-user system.
+            fs::remove_all(full_path);
+        }
+        catch (const std::system_error& e)
+        {
+            std::string error = "Directory exists and cannot be removed. Reason: ";
+            error.append(e.what());
+            throw armnn::RuntimeException(error);
+        }
     }
-
 #if defined(_WIN32)
     result = _mkdir(full_path.c_str()); // can be used on Windows
     armnn::ConditionalThrow<armnn::RuntimeException>((result == 0), "Was unable to create temporary directory");
 #else
-    result = mkdir(full_path.c_str(), permissions);
-    armnn::ConditionalThrow<armnn::RuntimeException>((result == 0), "Was unable to create temporary directory");
+    try
+    {
+        if(!fs::create_directory(full_path))
+        {
+            throw armnn::RuntimeException("Unable to create directory: " + full_path);
+        }
+    }
+    catch (const std::system_error& e)
+    {
+        std::string error = "Unable to create directory. Reason: ";
+        error.append(e.what());
+        throw armnn::RuntimeException(error);
+    }
 #endif
 
     return full_path + "/";
 }
 
 FileContents ReadFileContentsIntoString(const std::string path) {
+    if (!fs::exists(path))
+    {
+        throw armnn::RuntimeException("Path does not exist: " + path);
+    }
     std::ifstream input_file(path);
     armnn::ConditionalThrow<armnn::RuntimeException>((input_file.is_open()), "Could not read file contents");
     return FileContents((std::istreambuf_iterator<char>(input_file)), std::istreambuf_iterator<char>());
diff --git a/src/backends/backendsCommon/test/layerTests/DebugTestImpl.cpp b/src/backends/backendsCommon/test/layerTests/DebugTestImpl.cpp
index e2fa8db..2ec94bc 100644
--- a/src/backends/backendsCommon/test/layerTests/DebugTestImpl.cpp
+++ b/src/backends/backendsCommon/test/layerTests/DebugTestImpl.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
+// Copyright © 2017,2023 Arm Ltd and Contributors. All rights reserved.
 // SPDX-License-Identifier: MIT
 //
 
@@ -72,9 +72,8 @@
     {
         // Given that this is dependent on an ExNet switch, we need to explicitly set the directory that the
         //  files are stored in as this happens within the ExNet flow
-        fs::path tmpDir = fs::temp_directory_path();
-        armnnUtils::Filesystem::CreateDirectory("/ArmNNIntermediateLayerOutputs");
-        std::string full_path = tmpDir.generic_string() + "/ArmNNIntermediateLayerOutputs/" + layerName + ".numpy";
+        auto tmpDir = armnnUtils::Filesystem::CreateDirectory("/ArmNNIntermediateLayerOutputs");
+        std::string full_path = tmpDir + layerName + ".numpy";
 
         ExecuteWorkload(*workload, memoryManager);