[UnitTest/Buffer] Revise buffer impl. and add its unittests
authorDongju Chae <dongju.chae@samsung.com>
Thu, 16 Apr 2020 10:49:23 +0000 (19:49 +0900)
committer송욱/On-Device Lab(SR)/Staff Engineer/삼성전자 <wook16.song@samsung.com>
Mon, 20 Apr 2020 08:40:12 +0000 (17:40 +0900)
This patch revises buffer impl. and add its unittests

Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
src/core/ne-buffer.cc
src/core/ne-buffer.h
src/core/ne-handler.cc
src/core/ne-hwmem.cc
src/core/ne-model.cc
tests/unittests/meson.build
tests/unittests/ne_core_buffer_test.cc [new file with mode: 0644]

index a04cab2..87359f7 100644 (file)
@@ -32,10 +32,11 @@ Buffer::~Buffer ()
 /**
  * @brief create input/output tensors using metadata
  * @param[in] meta [OPTIONAL] the metadata of model
+ * @return 0 if no error, otherwise a negative errno
  * @note if metadata is not provided, this assumes that
  *       there is a single pair of input/output tensors.
  */
-void Buffer::createTensors (const Metadata *meta)
+int Buffer::createTensors (const Metadata *meta)
 {
   if (meta != nullptr) {
     num_input_ = meta->getInputNum ();
@@ -51,17 +52,38 @@ void Buffer::createTensors (const Metadata *meta)
   for (uint32_t i = 0; i < num_output_; i++)
     output_.push_back (new HWmem (new HWmemChunk));
 
+  int status = 0;
   if (meta != nullptr) {
-    for (uint32_t i = 0; i < meta->getInputNum(); i++)
-      setInputTensor (i, meta->getInputOffset (i),
+    for (uint32_t i = 0; i < meta->getInputNum(); i++) {
+      status = setInputTensor (i, meta->getInputOffset (i),
           meta->getInputTensorSize (i, DATA_LAYOUT_SRNPU));
-    for (uint32_t i = 0; i < meta->getOutputNum(); i++)
-      setOutputTensor (i, meta->getOutputOffset (i),
+      if (status != 0) {
+        logerr (TAG, "failed to set input tensor: index (%d)\n", i);
+        return status;
+      }
+    }
+    for (uint32_t i = 0; i < meta->getOutputNum(); i++) {
+      status = setOutputTensor (i, meta->getOutputOffset (i),
           meta->getOutputTensorSize (i, DATA_LAYOUT_SRNPU));
+      if (status != 0) {
+        logerr (TAG, "failed to set output tensor: index (%d)\n", i);
+        return status;
+      }
+    }
   } else {
-    setInputTensor (0, 0, getSize());
-    setOutputTensor (0, 0, getSize());
+    status = setInputTensor (0, 0, getSize());
+    if (status != 0) {
+      logerr (TAG, "failed to set input tensor: index (0)\n");
+      return status;
+    }
+    status = setOutputTensor (0, 0, getSize());
+    if (status != 0) {
+      logerr (TAG, "failed to set output tensor: index (0)\n");
+      return status;
+    }
   }
+
+  return 0;
 }
 
 /**
@@ -69,17 +91,25 @@ void Buffer::createTensors (const Metadata *meta)
  * @param[in] idx tensor index
  * @param[in] offset tensor offset in the base buffer
  * @param[in] size tensor size
+ * @return 0 if no error. otherwise a negative errno
  */
-void
+int
 Buffer::setInputTensor (uint32_t idx, size_t offset, size_t size) {
+  if (size == 0) {
+    logerr (TAG, "Zero size was given\n");
+    return -EINVAL;
+  }
+
   if (idx < num_input_) {
     input_[idx]->setParent (this);
     input_[idx]->setOffset (offset);
     if (input_[idx]->alloc (size) != 0) {
       logerr (TAG, "failed to allocate output tensor %d\n", idx);
-      throw std::bad_alloc();
+      return -ENOMEM;
     }
   }
+
+  return 0;
 }
 
 /**
@@ -87,17 +117,25 @@ Buffer::setInputTensor (uint32_t idx, size_t offset, size_t size) {
  * @param[in] idx tensor index
  * @param[in] offset tensor offset in the base buffer
  * @param[in] size tensor size
+ * @return 0 if no error. otherwise a negative errno
  */
-void
+int
 Buffer::setOutputTensor (uint32_t idx, size_t offset, size_t size) {
+  if (size == 0) {
+    logerr (TAG, "Zero size was given\n");
+    return -EINVAL;
+  }
+
   if (idx < num_output_) {
     output_[idx]->setParent (this);
     output_[idx]->setOffset (offset);
     if (output_[idx]->alloc (size) != 0) {
       logerr (TAG, "failed to allocate output tensor %d\n", idx);
-      throw std::bad_alloc();
+      return -ENOMEM;
     }
   }
+
+  return 0;
 }
 
 /**
index 375685c..3b58af1 100644 (file)
@@ -26,10 +26,7 @@ class Buffer : public HWmem {
     Buffer (const HWmemImpl* impl);
     ~Buffer ();
 
-    void createTensors (const Metadata *meta = nullptr);
-
-    void setInputTensor (uint32_t idx, size_t offset, size_t size);
-    void setOutputTensor (uint32_t idx, size_t offset, size_t size);
+    int createTensors (const Metadata *meta = nullptr);
 
     HWmem *getInputTensor (uint32_t idx);
     HWmem *getOutputTensor (uint32_t idx);
@@ -38,6 +35,9 @@ class Buffer : public HWmem {
     uint32_t getOutputNum () { return num_output_; }
 
   private:
+    int setInputTensor (uint32_t idx, size_t offset, size_t size);
+    int setOutputTensor (uint32_t idx, size_t offset, size_t size);
+
     std::vector <HWmem *> input_;     /**< input tensors */
     std::vector <HWmem *> output_;    /**< output tensors */
 
index 828aadf..13a1896 100644 (file)
@@ -174,32 +174,32 @@ delete_exit:
 
       Buffer * buffer = mem_->allocBuffer ();
       if (buffer != nullptr) {
+        int status;
+
         if (first->type == BUFFER_DMABUF) {
           buffer->setDmabuf (first->dmabuf);
           buffer->setOffset (first->offset);
           buffer->setSize (meta->getBufferSize());
         } else {
-          int status = buffer->alloc (meta->getBufferSize ());
+          status = buffer->alloc (meta->getBufferSize ());
           if (status != 0) {
             logerr (TAG, "Failed to allocate buffer: %d\n", status);
-            delete buffer;
-            return nullptr;
+            goto delete_buffer;
           }
         }
-      }
 
-      try {
-        buffer->createTensors (meta);
-      } catch (std::bad_alloc& bad) {
-        logerr (TAG, "Failed to allocate buffer: No enough memory\n");
-        delete buffer;
-        buffer = nullptr;
-      } catch (std::exception& exp) {
-        logerr (TAG, "Failed to allocate buffer: %s\n", exp.what());
-        delete buffer;
-        buffer = nullptr;
+        status = buffer->createTensors (meta);
+        if (status != 0) {
+          logerr (TAG, "Failed to allocate tensors: %s\n", status);
+          goto delete_buffer;
+        }
       }
+
       return buffer;
+
+delete_buffer:
+      delete buffer;
+      return nullptr;
     }
 
     int run (npu_input_opmode opmode, const Model *model,
index 730d0b8..5e43f21 100644 (file)
@@ -90,7 +90,7 @@ HWmemDevice::cleanup (HWmem *hwmem) const
   if (hwmem->getDmabuf () >= 0) {
     hwmem->getDriverAPI()->dealloc (hwmem->getDmabuf ());
 
-    hwmem->setDmabuf (0);
+    hwmem->setDmabuf (-1);
     hwmem->setSize (0);
   }
 }
@@ -137,6 +137,8 @@ HWmemDevice::dealloc (HWmem *hwmem) const
     return -EINVAL;
 
   hwmem->getDriverAPI()->dealloc (hwmem->getDmabuf());
+  hwmem->setDmabuf (-1);
+  hwmem->setSize (0);
   return 0;
 }
 
index f7808e9..4ce25b4 100644 (file)
@@ -64,6 +64,7 @@ Metadata::extractMetadata (void *data)
   }
 
   if (meta_ins != nullptr && !meta_ins->checkSanity ()) {
+    logerr (TAG, "Failed to pass the metadata sanity check\n");
     delete meta_ins;
     meta_ins = nullptr;
   }
index 1eb1dc5..3713b16 100644 (file)
@@ -41,6 +41,15 @@ if gtest_dep.found()
   )
   test('unittest_ne_core_model', unittest_ne_core_model)
 
+  unittest_ne_core_buffer = executable('unittest_ne_core_buffer',
+    ['ne_core_buffer_test.cc'],
+    dependencies: [gtest_dep, ne_core_dep],
+    install : true,
+    install_rpath : ne_libdir,
+    install_dir : join_paths(ne_bindir, 'unittests')
+  )
+  test('unittest_ne_core_buffer', unittest_ne_core_buffer)
+
 #  unittest_ne_core_handler = executable('unittest_ne_core_handler',
 #    ['ne_core_handler_test.cpp'],
 #    include_directories: ne_host_inc,
diff --git a/tests/unittests/ne_core_buffer_test.cc b/tests/unittests/ne_core_buffer_test.cc
new file mode 100644 (file)
index 0000000..031f736
--- /dev/null
@@ -0,0 +1,192 @@
+/**
+ * Proprietary
+ * Copyright (C) 2020 Samsung Electronics
+ * Copyright (C) 2020 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file ne_core_buffer_test.cc
+ * @date 14 Apr 2020
+ * @brief Test functionality of buffer
+ * @author Dongju Chae <dongju.chae@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <ne-buffer.h>
+
+#include "ne_unittest_utils.h"
+
+/**
+ * @brief fill dummy metadata v1
+ */
+static void
+fill_test_metadata_v1 (npubin_meta & data, uint32_t input_size, uint32_t output_size)
+{
+  data.magiccode = NPUBIN_MAGICCODE | 0x1;  /* v1 */
+  data.size = 8192;
+  data.buffer_size = 4096;
+  data.program_size = 2048;
+  data.weight_size = 2048;
+
+  data.input_offset = 0;
+  data.input_size = input_size;
+  data.output_offset = 2048;
+  data.output_size = output_size;
+}
+
+/**
+ * @brief fill dummy metadata v2
+ */
+static void
+fill_test_metadata_v2 (npubin_meta & data, uint32_t input_num, uint32_t output_num)
+{
+  data.magiccode = NPUBIN_MAGICCODE | 0x2;  /* v2 */
+  data.size = 8192;
+  data.buffer_size = 4096;
+  data.program_size = 2048;
+  data.weight_size = 2048;
+
+  data.input_num = input_num;
+  for (uint32_t i = 0; i < data.input_num; i++) {
+    data.input_offsets[i] = 100 * (i + 1);
+    data.input_elem_size[i] = 1 * (i + 1);
+    for (uint32_t j = 0; j < MAX_RANK; j++)
+      data.input_dims[i][j] = 2 * (i + 1);
+    data.input_quant_z[i] = 3 * (i + 1);
+    data.input_quant_s[i] = 4 * (i + 1);
+  }
+
+  data.output_num = output_num;
+  for (uint32_t i = 0; i < data.output_num; i++) {
+    data.output_offsets[i] = 100 * (i + 1);
+    data.output_elem_size[i] = 1 * (i + 1);
+    for (uint32_t j = 0; j < MAX_RANK; j++)
+      data.output_dims[i][j] = 2 * (i + 1);
+    data.output_quant_z[i] = 3 * (i + 1);
+    data.output_quant_s[i] = 4 * (i + 1);
+  }
+}
+/**
+ * @brief check buffer primitives
+ */
+TEST (ne_core_buffer_test, buffer_primitives)
+{
+  std::unique_ptr<DriverAPI> api;
+  api = DriverAPI::createDriverAPI (NPUCOND_TRIV_CONN_SOCIP, 0);
+  ASSERT_NE (api.get(), nullptr);
+
+  std::unique_ptr<Buffer> buffer (new Buffer (new HWmemDevice));
+  buffer->setDriverAPI (api.get ());
+
+  uint32_t size = 4096;
+  EXPECT_EQ (buffer->alloc (size), 0);
+  EXPECT_EQ (buffer->getSize (), size);
+  EXPECT_EQ (api.get(), buffer->getDriverAPI ());
+
+  /** it's okay even if metadata is nullptr */
+  EXPECT_EQ (buffer->createTensors (nullptr), 0);
+  EXPECT_EQ (buffer->getInputNum (), 1);
+  EXPECT_EQ (buffer->getOutputNum (), 1);
+  EXPECT_EQ (buffer->getInputTensor (0)->getSize (), size);
+  EXPECT_EQ (buffer->getOutputTensor (0)->getSize (), size);
+
+  npubin_meta data;
+  /** with metadata v1 */
+  uint32_t input_size = 1024;
+  uint32_t output_size = 2048;
+  fill_test_metadata_v1 (data, input_size, output_size);
+
+  std::unique_ptr<Metadata> meta = Metadata::extractMetadata (&data);
+  EXPECT_EQ (buffer->createTensors (meta.get()), 0);
+  EXPECT_EQ (buffer->getInputNum (), 1);
+  EXPECT_EQ (buffer->getOutputNum (), 1);
+  EXPECT_EQ (buffer->getInputTensor (0)->getSize (), input_size);
+  EXPECT_EQ (buffer->getOutputTensor (0)->getSize (), output_size);
+
+  /** with metadata v2 */
+  uint32_t input_num = 2;
+  uint32_t output_num = 3;
+  fill_test_metadata_v2 (data, input_num, output_num);
+
+  meta = Metadata::extractMetadata (&data);
+  size = data.size;
+
+  std::unique_ptr<Buffer> buffer2 (new Buffer (new HWmemDevice));
+  buffer2->setDriverAPI (api.get ());
+
+  EXPECT_EQ (buffer2->alloc (size), 0);
+  EXPECT_EQ (buffer2->getSize (), size);
+  EXPECT_EQ (buffer2->createTensors (meta.get()), 0);
+  EXPECT_EQ (buffer2->getInputNum (), input_num);
+  EXPECT_EQ (buffer2->getOutputNum (), output_num);
+
+  for (uint32_t i = 0; i < data.input_num; i++)
+    EXPECT_EQ (buffer2->getInputTensor (i)->getSize (),
+        meta->getInputTensorSize (i, DATA_LAYOUT_SRNPU));
+  for (uint32_t i = 0; i < data.output_num; i++)
+    EXPECT_EQ (buffer2->getOutputTensor (i)->getSize (),
+        meta->getOutputTensorSize (i, DATA_LAYOUT_SRNPU));
+}
+
+/**
+ * @brief check buffer alloc with error handling
+ */
+TEST (ne_core_buffer_test, buffer_alloc_no_drv_api_n)
+{
+  std::unique_ptr<Buffer> buffer (new Buffer (new HWmemDevice));
+
+  uint32_t size = 4096;
+  EXPECT_NE (buffer->alloc (size), 0);
+  EXPECT_NE (buffer->getSize (), size);
+}
+
+/**
+ * @brief check buffer's createTensors() with error handling
+ */
+TEST (ne_core_buffer_test, buffer_create_tensors_n)
+{
+  std::unique_ptr<DriverAPI> api;
+  api = DriverAPI::createDriverAPI (NPUCOND_TRIV_CONN_SOCIP, 0);
+  ASSERT_NE (api.get(), nullptr);
+
+  uint32_t size = 4096;
+  std::unique_ptr<Buffer> buffer (new Buffer (new HWmemDevice));
+  buffer->setDriverAPI (api.get());
+  ASSERT_EQ (buffer->alloc (size), 0);
+
+  /** createTensors() after dealloc the base buffer */
+  EXPECT_EQ (buffer->dealloc (), 0);
+  EXPECT_NE (buffer->createTensors (nullptr), 0);
+}
+
+/**
+ * @brief check buffer getInput/OutputTensor with error handling
+ */
+TEST (ne_core_buffer_test, buffer_get_tensor_n)
+{
+  std::unique_ptr<DriverAPI> api;
+  api = DriverAPI::createDriverAPI (NPUCOND_TRIV_CONN_SOCIP, 0);
+  ASSERT_NE (api.get(), nullptr);
+
+  std::unique_ptr<Buffer> buffer (new Buffer (new HWmemDevice));
+  buffer->setDriverAPI (api.get ());
+
+  uint32_t input_num = 2;
+  uint32_t output_num = 3;
+
+  npubin_meta data;
+  fill_test_metadata_v2 (data, input_num, output_num);
+  ASSERT_EQ (buffer->alloc (data.buffer_size), 0);
+
+  /** invalid index */
+  EXPECT_EQ (buffer->getInputTensor (input_num + 1), nullptr);
+  EXPECT_EQ (buffer->getOutputTensor (output_num + 1), nullptr);
+}
+
+/**
+ * @brief main function for unit test
+ */
+int
+main (int argc, char **argv)
+{
+  return start_gtest (argc, argv);
+}