IVGCVSW-4271 Add getSupportedOperations marker file

* Also fix segfault in previous graph dump code
* Cherry picked from 19.08 (https://jira.arm.com/browse/IVGCVSW-4297)

Change-Id: Id4f2e89f5126b7962b4439370e6dd3edcee96f51
Signed-off-by: Jim Flynn <jim.flynn@arm.com>
diff --git a/Utils.cpp b/Utils.cpp
index 229f35b..49a4c79 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -11,7 +11,13 @@
 #include <Permute.hpp>
 
 #include <cassert>
+#include <cerrno>
 #include <cinttypes>
+#include <sstream>
+#include <cstdio>
+#include <time.h>
+
+
 
 using namespace android;
 using namespace android::hardware;
@@ -246,6 +252,11 @@
             dumpElementFunction = &DumpTensorElement<int32_t>;
             break;
         }
+        case armnn::DataType::Float16:
+        {
+            dumpElementFunction = &DumpTensorElement<armnn::Half>;
+            break;
+        }
         default:
         {
             dumpElementFunction = nullptr;
@@ -358,10 +369,91 @@
     profiler->Print(fileStream);
 }
 
+std::string ExportNetworkGraphToDotFile(const armnn::IOptimizedNetwork& optimizedNetwork,
+                                        const std::string& dumpDir)
+{
+    std::string fileName;
+    // The dump directory must exist in advance.
+    if (dumpDir.empty())
+    {
+        return fileName;
+    }
+
+    std::string timestamp = GetFileTimestamp();
+    if (timestamp.empty())
+    {
+        return fileName;
+    }
+
+    // Set the name of the output .dot file.
+    fileName = boost::str(boost::format("%1%/%2%_networkgraph.dot")
+                          % dumpDir
+                          % timestamp);
+
+    ALOGV("Exporting the optimized network graph to file: %s", fileName.c_str());
+
+    // Write the network graph to a dot file.
+    std::ofstream fileStream;
+    fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
+
+    if (!fileStream.good())
+    {
+        ALOGW("Could not open file %s for writing", fileName.c_str());
+        return fileName;
+    }
+
+    if (optimizedNetwork.SerializeToDot(fileStream) != armnn::Status::Success)
+    {
+        ALOGW("An error occurred when writing to file %s", fileName.c_str());
+    }
+    return fileName;
+}
+
 bool IsDynamicTensor(const armnn::TensorInfo& outputInfo)
 {
     // Dynamic tensors have at least one 0-sized dimension
     return outputInfo.GetNumElements() == 0u;
 }
 
+std::string GetFileTimestamp()
+{
+    // used to get a timestamp to name diagnostic files (the ArmNN serialized graph
+    // and getSupportedOperations.txt files)
+    timespec ts;
+    int iRet = clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+    std::stringstream ss;
+    if (iRet == 0)
+    {
+        ss << std::to_string(ts.tv_sec) << "_" << std::to_string(ts.tv_nsec);
+    }
+    else
+    {
+        ALOGW("clock_gettime failed with errno %s : %s", std::to_string(errno).c_str(), std::strerror(errno));
+    }
+    return ss.str();
+}
+
+void RenameGraphDotFile(const std::string& oldName, const std::string& dumpDir, const armnn::NetworkId networkId)
+{
+    if (dumpDir.empty())
+    {
+        return;
+    }
+    if (oldName.empty())
+    {
+        return;
+    }
+    const std::string newFileName = boost::str(boost::format("%1%/%2%_networkgraph.dot")
+                                               % dumpDir
+                                               % std::to_string(networkId));
+    int iRet = rename(oldName.c_str(), newFileName.c_str());
+    if (iRet != 0)
+    {
+        std::stringstream ss;
+        ss << "rename of [" << oldName << "] to [" << newFileName << "] failed with errno " << std::to_string(errno)
+           << " : " << std::strerror(errno);
+        ALOGW(ss.str().c_str());
+    }
+}
+
 } // namespace armnn_driver