IVGCVSW-5980 JSON profiling output

 * Add new ProfilingDetails class to construct operator details string
 * Add new macro which helps append layer details to ostream
 * Add ProfilingEnabled to NetworkProperties so that profiling can be
   realised when loading the network
 * Add further optional info to WorkloadInfo specific to convolutions
 * Generalise some JsonPrinter functions into JsonUtils for reusability
 * Remove explicit enabling of profiling within InferenceModel as it is
   done when loading network
 * Add ProfilingDetails macros to ConvolutionWorkloads for validation

Signed-off-by: Keith Davis <keith.davis@arm.com>
Change-Id: Ie84bc7dc667e72e6bcb635544f9ead7af1765690
diff --git a/src/backends/neon/workloads/NeonConvolution2dWorkload.cpp b/src/backends/neon/workloads/NeonConvolution2dWorkload.cpp
index 32af3f8..1e12e13 100644
--- a/src/backends/neon/workloads/NeonConvolution2dWorkload.cpp
+++ b/src/backends/neon/workloads/NeonConvolution2dWorkload.cpp
@@ -74,8 +74,6 @@
 
     m_Data.ValidateInputsOutputs("NeonConvolution2dWorkload", 1, 1);
 
-    // todo: check tensor shapes match.
-
     arm_compute::ITensor& input = PolymorphicDowncast<IAclTensorHandle*>(m_Data.m_Inputs[0])->GetTensor();
     arm_compute::ITensor& output = PolymorphicDowncast<IAclTensorHandle*>(m_Data.m_Outputs[0])->GetTensor();
 
@@ -120,6 +118,23 @@
                                                  activationInfo,
                                                  isFastMathEnabled);
 
+    // Add details for profiling output
+    std::string workloadName = "NeonConvolution2dWorkload_Execute_Guid" + std::to_string(this->GetGuid());
+
+    WorkloadInfo detailsInfo;
+
+    detailsInfo.m_InputTensorInfos = info.m_InputTensorInfos;
+    detailsInfo.m_OutputTensorInfos = info.m_OutputTensorInfos;
+    detailsInfo.m_WeightsTensorInfo = armnn::Optional<armnn::TensorInfo>(descriptor.m_Weight->GetTensorInfo());
+    detailsInfo.m_ConvolutionMethod = armnn::Optional<std::string>(GetConvolutionMethodString());
+    if (descriptor.m_Parameters.m_BiasEnabled)
+    {
+        detailsInfo.m_BiasTensorInfo = armnn::Optional<armnn::TensorInfo>(descriptor.m_Bias->GetTensorInfo());
+    }
+
+    // Report Profiling Details
+    ARMNN_REPORT_PROFILING_WORKLOAD_DESC(workloadName, descriptor.m_Parameters, detailsInfo);
+
     m_ConvolutionLayer.reset(convolutionLayer.release());
 
     ARMNN_ASSERT(m_ConvolutionLayer);
@@ -146,6 +161,23 @@
     return m_ConvolutionMethod;
 }
 
+std::string NeonConvolution2dWorkload::GetConvolutionMethodString()
+{
+    switch ( m_ConvolutionMethod )
+    {
+        case arm_compute::ConvolutionMethod::FFT:
+            return "FFT";
+        case arm_compute::ConvolutionMethod::DIRECT:
+            return "Direct";
+        case arm_compute::ConvolutionMethod::GEMM:
+            return "GEMM";
+        case arm_compute::ConvolutionMethod::WINOGRAD:
+            return "Winograd";
+        default:
+            return "Unknown";
+    }
+}
+
 void NeonConvolution2dWorkload::FreeUnusedTensors()
 {
     FreeTensorIfUnused(m_KernelTensor);