IVGCVSW-4270 Change .dot file name to start with netId

* .dot file will start with the same number as the associated input
  and output tensor dump files

Change-Id: Ic64539854c2b8c34a7034fa18a142b2dfe67df7d
Signed-off-by: Jim Flynn <jim.flynn@arm.com>

IVGCVSW-4271 Add getSupportedOperations marker file

* Also fix segfault in previous graph dump code

Change-Id: I01915f47ae17d89ad3595430d073a17b1a1fda58
Signed-off-by: Jim Flynn <jim.flynn@arm.com>
diff --git a/Utils.cpp b/Utils.cpp
index 6ee025e..3583d62 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -13,7 +13,13 @@
 #include <armnn/Utils.hpp>
 
 #include <cassert>
+#include <cerrno>
 #include <cinttypes>
+#include <sstream>
+#include <cstdio>
+#include <time.h>
+
+
 
 using namespace android;
 using namespace android::hardware;
@@ -359,10 +365,93 @@
     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