[UTILS] Add a feature of trinity-smi to show list of apps/tasks
authorDongju Chae <dongju.chae@samsung.com>
Fri, 20 Nov 2020 02:11:06 +0000 (11:11 +0900)
committer송욱/On-Device Lab(SR)/Staff Engineer/삼성전자 <wook16.song@samsung.com>
Tue, 24 Nov 2020 01:46:30 +0000 (10:46 +0900)
This patch adds a feature of trinity-smi to show list of apps/tasks

Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
include/common/typedef.h
include/host/libnpuhost.h
src/core/ne-handler.cc
src/core/ne-handler.h
src/core/npu/NPUdrvAPI.h
src/core/npu/NPUdrvAPI_triv.cc
src/core/npu/NPUdrvAPI_triv2.cc
utils/trinity_smi/trinity-smi.cc

index 8553bb8..9acdbfc 100644 (file)
@@ -151,4 +151,60 @@ typedef enum {
   SMODEL_OPS_END,
 } model_opmode;
 
+/**
+ * @brief Description of npu app (per device open) status
+ */
+typedef enum {
+  NPU_APP_UNKNOWN = 0,
+  NPU_APP_ERROR = 1,
+  NPU_APP_PENDING = 2,
+  NPU_APP_STARTED = 3,
+  NPU_APP_TERMINATED = 4,
+} npu_app_status;
+
+/**
+ * @brief Description of npu task (per inference) status
+ */
+typedef enum {
+  NPU_TASK_UNKNOWN = 0,
+  NPU_TASK_ERROR = 1,
+  NPU_TASK_PENDING = 2,
+  NPU_TASK_RUNNING = 3,
+  NPU_TASK_FINISHED = 4,
+} npu_task_status;
+
+typedef struct {
+  int appid;
+  char name[16];
+
+  npu_app_status status;
+
+  uint32_t num_total_tasks;
+  uint32_t num_active_tasks;
+
+  uint64_t total_alloc_mem;
+  uint64_t total_freed_mem;
+} npu_stat_app;
+
+typedef struct {
+  uint32_t num;
+  npu_stat_app *stat;
+} npu_stat_apps;
+
+typedef struct {
+  int taskid;
+  uint64_t modelid;
+
+  npu_priority priority;
+  npu_task_status status;
+
+  uint32_t sched_time;
+  uint32_t infer_time;
+} npu_stat_task;
+
+typedef struct {
+  uint32_t num;
+  npu_stat_task *stat;
+} npu_stat_tasks;
+
 #endif /* NPU_TYPEDEF_H__ */
index 693b333..5a6e54d 100644 (file)
@@ -460,7 +460,7 @@ int allocNPU_genericBuffers (npudev_h dev, generic_buffers * buffers);
  */
 int cleanNPU_genericBuffers (npudev_h dev, generic_buffers * buffers);
 
-/** NPU Profiling */
+/** NPU Profiling (both for emulated/real-device envionment) */
 
 #define NPU_OPNAME_MAX (32)
 
@@ -495,6 +495,39 @@ int getNPU_profile (npudev_h dev, int task_id, npu_profile *profile);
  */
 void putNPU_profile (npu_profile *profile);
 
+/** NPU Statistics (only for real-device envionment) */
+
+/**
+ * @brief get the stats for the latest apps of the target device
+ * @param[in] dev The NPU device handle
+ * @param[out] stat The list of app stat
+ * @note The caller has the responsibility to free the resources.
+ *       This API is not working on the emulated envionment.
+ */
+int getNPU_statApps (npudev_h dev, npu_stat_apps *stat);
+
+/**
+ * @brief Free the stat instance obtained by getNPU_statApps().
+ * @param[in] stat Stat instance
+ */
+void putNPU_statApps (npu_stat_apps *stat);
+
+/**
+ * @brief get the stats for the latest tasks of the target app
+ * @param[in] dev The NPU device handle
+ * @param[in] appid The identifier of target app
+ * @param[out] stat The list of task stat
+ * @note The caller has the responsibility to free the resources.
+ *       This API is not working on the emulated envionment.
+ */
+int getNPU_statTasks (npudev_h dev, int appid, npu_stat_tasks *stat);
+
+/**
+ * @brief Free the stat instance obtained by getNPU_statTasks().
+ * @param[in] stat Stat instance
+ */
+void putNPU_statTasks (npu_stat_tasks *stat);
+
 #if defined(__cplusplus)
 }
 #endif
index 655302f..1537a4a 100644 (file)
@@ -160,6 +160,77 @@ void putNPU_profile (npu_profile *profile)
 }
 
 /**
+ * @brief get the stats for the latest apps of the target device
+ * @param[in] dev The NPU device handle
+ * @param[out] stat The instance of app stat
+ * @note The caller has the responsibility to free the resources.
+ *       This API is not working on the emulated envionment.
+ */
+int getNPU_statApps (npudev_h dev, npu_stat_apps *stat)
+{
+  INIT_HOST_HANDLER (host_handler, dev);
+
+#ifdef ENABLE_EMUL
+  logwarn (TAG, "we don't support statistics for the emulated envionment");
+  if (stat) {
+    stat->num = 0;
+    stat->stat = nullptr;
+  }
+  return 0;
+#else
+  return host_handler->getStatApps (stat);
+#endif
+}
+
+/**
+ * @brief Free the stat instance obtained by getNPU_statApps().
+ * @param[in] stat Stat instance
+ */
+void putNPU_statApps (npu_stat_apps *stat)
+{
+  if (stat) {
+    delete [] stat->stat;
+    stat->num = 0;
+  }
+}
+
+/**
+ * @brief get the stats for the latest tasks of the target app
+ * @param[in] dev The NPU device handle
+ * @param[in] appid The identifier of target app
+ * @param[out] stat The instance of task stat
+ * @note The caller has the responsibility to free the resources.
+ *       This API is not working on the emulated envionment.
+ */
+int getNPU_statTasks (npudev_h dev, int appid, npu_stat_tasks *stat)
+{
+  INIT_HOST_HANDLER (host_handler, dev);
+
+#ifdef ENABLE_EMUL
+  logwarn (TAG, "we don't support statistics for the emulated envionment");
+  if (stat) {
+    stat->num = 0;
+    stat->stat = nullptr;
+  }
+  return 0;
+#else
+  return host_handler->getStatTasks (appid, stat);
+#endif
+}
+
+/**
+ * @brief Free the stat instance obtained by getNPU_statTasks().
+ * @param[in] stat Stat instance
+ */
+void putNPU_statTasks (npu_stat_tasks *stat)
+{
+  if (stat) {
+    delete [] stat->stat;
+    stat->num = 0;
+  }
+}
+
+/**
  * @brief Send the NN model to NPU.
  * @param[in] dev The NPU device handle
  * @param[in] modelfile The filepath to the compiled NPU NN model in any buffer_type
@@ -640,6 +711,37 @@ HostHandler::getProfile (int task_id, npu_profile *profile)
 }
 
 /**
+ * @brief get the stats for the latest apps of the target device
+ * @param[out] stat The list of app stat
+ * @note The caller has the responsibility to free the resources.
+ *       This API is not working on the emulated envionment.
+ */
+int
+HostHandler::getStatApps (npu_stat_apps *stat)
+{
+  const DriverAPI * api = device_->getDriverAPI ();
+  assert (api != nullptr);
+
+  return api->getStatApps (stat);
+}
+
+/**
+ * @brief get the stats for the latest tasks of the target app
+ * @param[in] appid The identifier of target app
+ * @param[out] stat The list of task stat
+ * @note The caller has the responsibility to free the resources.
+ *       This API is not working on the emulated envionment.
+ */
+int
+HostHandler::getStatTasks (int appid, npu_stat_tasks *stat)
+{
+  const DriverAPI * api = device_->getDriverAPI ();
+  assert (api != nullptr);
+
+  return api->getStatTasks (appid, stat);
+}
+
+/**
  * @brief Get the driver API level of opened NPU device
  * @param[out] level driver API level
  * @return 0 if no error, otherwise a negative errno
index 630dbb1..115983e 100644 (file)
@@ -62,6 +62,9 @@ class HostHandler {
     int getMemoryStatus (size_t *alloc_total, size_t *free_total);
     int getDeviceStatus (npu_status *status, uint32_t *num_requests);
 
+    int getStatApps (npu_stat_apps *stat);
+    int getStatTasks (int appid, npu_stat_tasks *stat);
+
     static int getNumDevices (dev_type type);
     static int getDevice (npudev_h *dev, dev_type type, uint32_t id);
 
index 3e38f1b..b8a1d74 100644 (file)
@@ -110,6 +110,9 @@ class DriverAPI {
     virtual int getProfile (int task_id, void **profile_buf,
         size_t *profile_size) const { return -EPERM; }
 
+    virtual int getStatApps (npu_stat_apps *stat) const { return -EPERM; }
+    virtual int getStatTasks (int appid, npu_stat_tasks *stat) const { return -EPERM; }
+
   protected:
     int dev_id_;  /**< device id. assume that 0 <= id < getNUmDevices() */
     int dev_fd_;  /**< devide fd. opened in constructor and closed in destructor */
@@ -195,6 +198,9 @@ class TrinityVision2API : public DriverAPI {
     int getProfile (int task_id, void **profile_buf,
         size_t *profile_size) const;
 
+    int getStatApps (npu_stat_apps *stat) const;
+    int getStatTasks (int appid, npu_stat_tasks *stat) const;
+
   private:
     int getDrvVersion () const;
     static const std::string dev_node_base;
index 762df1c..18458c1 100644 (file)
@@ -145,12 +145,18 @@ uint32_t
 TrinityVisionAPI::numRequests () const
 {
   if (this->initialized()) {
-    uint32_t num_requests;
+    struct trinity_ioctl_stat_apps stat;
     int ret;
 
-    ret = ioctl (this->getDeviceFD (), TRINITY_IOCTL_NUM_REQUESTS, &num_requests);
-    if (ret == 0)
+    ret = ioctl (this->getDeviceFD (), TRINITY_IOCTL_STAT_APPS, &stat);
+    if (ret == 0) {
+      uint32_t num_requests = 0;
+
+      for (uint32_t i = 0; i < stat.num_apps; i++)
+        num_requests += stat.stat[i].num_active_tasks;
+
       return num_requests;
+    }
   }
 
   return 0;
@@ -205,7 +211,7 @@ TrinityVisionAPI::dealloc (int dmabuf) const
 int
 TrinityVisionAPI::getMemoryStatus (size_t *alloc_total, size_t *free_total) const
 {
-  struct trinity_memory_stat stat;
+  struct trinity_ioctl_stat_app stat;
   int ret;
 
   if (!initialized())
@@ -214,7 +220,7 @@ TrinityVisionAPI::getMemoryStatus (size_t *alloc_total, size_t *free_total) cons
   if (alloc_total == nullptr || free_total == nullptr)
     return -EINVAL;
 
-  ret = ioctl (this->getDeviceFD (), TRINITY_IOCTL_MEMORY_STATUS, &stat);
+  ret = ioctl (this->getDeviceFD (), TRINITY_IOCTL_STAT_CURRENT_APP, &stat);
   if (ret < 0)
     return -errno;
 
index bded66e..f569903 100644 (file)
@@ -145,12 +145,18 @@ uint32_t
 TrinityVision2API::numRequests () const
 {
   if (this->initialized()) {
-    uint32_t num_requests;
+    struct trinity_ioctl_stat_apps stat;
     int ret;
 
-    ret = ioctl (this->getDeviceFD (), TRINITY_IOCTL_NUM_REQUESTS, &num_requests);
-    if (ret == 0)
+    ret = ioctl (this->getDeviceFD (), TRINITY_IOCTL_STAT_APPS, &stat);
+    if (ret == 0) {
+      uint32_t num_requests = 0;
+
+      for (uint32_t i = 0; i < stat.num_apps; i++)
+        num_requests += stat.stat[i].num_active_tasks;
+
       return num_requests;
+    }
   }
 
   return 0;
@@ -205,7 +211,7 @@ TrinityVision2API::dealloc (int dmabuf) const
 int
 TrinityVision2API::getMemoryStatus (size_t *alloc_total, size_t *free_total) const
 {
-  struct trinity_memory_stat stat;
+  struct trinity_ioctl_stat_app stat;
   int ret;
 
   if (!initialized())
@@ -214,7 +220,7 @@ TrinityVision2API::getMemoryStatus (size_t *alloc_total, size_t *free_total) con
   if (alloc_total == nullptr || free_total == nullptr)
     return -EINVAL;
 
-  ret = ioctl (this->getDeviceFD (), TRINITY_IOCTL_MEMORY_STATUS, &stat);
+  ret = ioctl (this->getDeviceFD (), TRINITY_IOCTL_STAT_CURRENT_APP, &stat);
   if (ret < 0)
     return -errno;
 
@@ -504,3 +510,88 @@ TrinityVision2API::getTops (uint32_t *tops) const
 
   return ret;
 }
+
+int
+TrinityVision2API::getStatApps (npu_stat_apps *stat) const
+{
+  struct trinity_ioctl_stat_apps stat_apps;
+  int ret;
+
+  if (!this->initialized())
+    return -EPERM;
+
+  if (stat == nullptr)
+    return -EINVAL;
+
+  stat->num = 0;
+  stat->stat = nullptr;
+
+  ret = ioctl (this->getDeviceFD (), TRINITY_IOCTL_STAT_APPS, &stat_apps);
+  if (ret != 0)
+    return -errno;
+
+  stat->num = stat_apps.num_apps;
+  if (stat->num == 0)
+    return 0;
+
+  stat->stat = new npu_stat_app[stat->num];
+
+  for (uint32_t i = 0; i < stat->num; i++) {
+    struct trinity_ioctl_stat_app *stat_app = &(stat_apps.stat[i]);
+
+    strncpy (stat->stat[i].name, stat_app->name, TRINITY_APP_NAME_MAX - 1);
+
+    stat->stat[i].appid = stat_app->appid;
+    stat->stat[i].status = static_cast<npu_app_status>(stat_app->status);
+
+    stat->stat[i].num_total_tasks = stat_app->num_total_tasks;
+    stat->stat[i].num_active_tasks = stat_app->num_active_tasks;
+
+    stat->stat[i].total_alloc_mem = stat_app->total_alloc_mem;
+    stat->stat[i].total_freed_mem = stat_app->total_freed_mem;
+  }
+
+  return 0;
+}
+
+int
+TrinityVision2API::getStatTasks (int appid, npu_stat_tasks *stat) const
+{
+  struct trinity_ioctl_stat_tasks stat_tasks;
+  int ret;
+
+  if (!this->initialized())
+    return -EPERM;
+
+  if (stat == nullptr)
+    return -EINVAL;
+
+  stat->num = 0;
+  stat->stat = nullptr;
+
+  stat_tasks.appid = appid;
+  ret = ioctl (this->getDeviceFD (), TRINITY_IOCTL_STAT_TASKS, &stat_tasks);
+  if (ret != 0)
+    return -errno;
+
+  stat->num = stat_tasks.num_tasks;
+  if (stat->num == 0)
+    return 0;
+
+  stat->stat = new npu_stat_task[stat->num];
+
+  for (uint32_t i = 0; i < stat->num; i++) {
+    struct trinity_ioctl_stat_task *stat_task = &(stat_tasks.stat[i]);
+
+    stat->stat[i].taskid = stat_task->taskid;
+    stat->stat[i].modelid = stat_task->modelid;
+
+    stat->stat[i].priority = static_cast<npu_priority>(stat_task->priority);
+    stat->stat[i].status = static_cast<npu_task_status>(stat_task->status);
+
+    stat->stat[i].sched_time = stat_task->sched_time;
+    stat->stat[i].infer_time = stat_task->infer_time;
+  }
+
+  return 0;
+}
index b9075fe..b19f396 100644 (file)
@@ -38,18 +38,32 @@ class TrinitySMI {
     }
     ~TrinitySMI () {}
 
-    uint32_t get_api_level ();
+    bool check_arguments (int argc, char ** argv);
 
     void append_version ();
-    void append_help ();
-    void append_devices ();
-    void append_processes ();
+    void append_stats ();
 
     void dump ();
 
+    void set_device (string node) { node_ = node; }
+    void set_appid (string appid) { appid_ = appid; }
+
   private:
+    uint32_t get_api_level ();
+    bool parse_device_node (dev_type & type, int & dev_id);
+    bool parse_app_id (int & app_id);
+    string to_hex_string (uint64_t val);
+
+    void append_help ();
+    void append_devices ();
+    void append_apps ();
+    void append_tasks ();
+
     string name_;
     stringstream ss_;
+
+    string appid_;
+    string node_;
 };
 
 /** @brief get driver api level from any device */
@@ -82,6 +96,70 @@ TrinitySMI::get_api_level ()
   return level;
 }
 
+/** @brief parse the given device node */
+bool
+TrinitySMI::parse_device_node (dev_type & type, int & dev_id)
+{
+  const char * last = strrchr (node_.c_str(), '-');
+
+  dev_id = -1;
+  if (last != NULL && *(last + 1) != '\x00') {
+    char *endptr;
+    errno = 0;
+    dev_id = strtol (last + 1, &endptr, 10);
+    if (errno != 0 || last + 1 == endptr)
+      dev_id = -1;
+  }
+
+  type = NPUCOND_CONN_UNKNOWN;
+  if (node_.find ("triv2-") != string::npos)
+    type = NPUCOND_TRIV2_CONN_SOCIP;
+  else if (node_.find ("triv-") != string::npos)
+    type = NPUCOND_TRIV_CONN_SOCIP;
+  else if (node_.find ("tria-") != string::npos)
+    type = NPUCOND_TRIA_CONN_SOCIP;
+
+  if (dev_id < 0 || type == NPUCOND_CONN_UNKNOWN) {
+    ss_ << "| Invalid device node provided: " << left << setw(29) << node_ << "|\n";
+    ss_ << "+------------------------------------------------------------+\n";
+
+    return false;
+  }
+
+  return true;
+}
+
+/** @brief parse the given app id */
+bool
+TrinitySMI::parse_app_id (int & app_id)
+{
+  const char *nptr = appid_.c_str();
+  char *endptr;
+
+  errno = 0;
+  app_id = strtol (nptr, &endptr, 10);
+
+  if (errno != 0 || nptr == endptr) {
+    ss_ << "| Invalid app id provided: " << left << setw(34) << appid_ << "|\n";
+    ss_ << "+------------------------------------------------------------+\n";
+
+    return false;
+  }
+  return true;
+}
+
+/** @brief convert a value to the hex string */
+string
+TrinitySMI::to_hex_string (uint64_t val)
+{
+  stringstream ss;
+
+  ss << "0x";
+  ss << hex << val;
+
+  return ss.str();
+}
+
 /** @brief show the version of npu-engine and driver */
 void
 TrinitySMI::append_version ()
@@ -104,18 +182,36 @@ TrinitySMI::append_version ()
   uint32_t api_level = get_api_level ();
 
   ss_ << std::ctime (&now_time);
-  ss_ << "+--------------------------------------------------------+\n";
-  ss_ << "| TRINITY-SMI: " << smi_ver;
-  ss_ << "  NPU-ENGINE: " << lib_ver;
-  ss_ << "  DRIVER-API: ";
+  ss_ << "+------------------------------------------------------------+\n";
+  ss_ << "|  Trinity System Management Interface                       |\n";
+  ss_ << "|  Copyright (C) Samsung Electronics Ltd.                    |\n";
+  ss_ << "|                                                            |\n";
+  ss_ << "|  TRINITY-SMI: " << smi_ver;
+  ss_ << "   NPU-ENGINE: " << lib_ver;
+  ss_ << "   DRIVER-API: ";
   ss_ << setw (3);
 #ifdef ENABLE_EMUL
   ss_ << "SIM";
 #else
   ss_ << (api_level == 0 ? "INV" : to_string (api_level));
 #endif
-  ss_ << " |\n";
-  ss_ << "+--------------------------------------------------------+\n";
+  ss_ << "  |\n";
+  ss_ << "+------------------------------------------------------------+\n";
+}
+
+/** @brief main routine to append stats */
+void
+TrinitySMI::append_stats ()
+{
+  if (!node_.empty ()) {
+    if (!appid_.empty ()) {
+      append_tasks ();
+    } else {
+      append_apps ();
+    }
+  } else {
+    append_devices ();
+  }
 }
 
 /** @brief show the list of trinity devices */
@@ -128,9 +224,10 @@ TrinitySMI::append_devices ()
     NPUCOND_TRIA_CONN_SOCIP
   };
 
-  ss_ << "+--------------------------------------------------------+\n";
-  ss_ << "| CLASS |  DEVICE NODE  | TOPS |  STATUS  |  # REQUESTS  |\n";
-  ss_ << "|========================================================|\n";
+  ss_ << "\n";
+  ss_ << "+------------------------------------------------------------+\n";
+  ss_ << "|  CLASS  |   DEVICE NODE   | TOPS |  STATUS  |  # REQUESTS  |\n";
+  ss_ << "|============================================================|\n";
 
   uint32_t count = 0;
   for (auto type : types) {
@@ -189,8 +286,8 @@ TrinitySMI::append_devices ()
       putNPUdevice (dev);
 #endif
 
-      ss_ << "| " << right << setw (5) << cls << " ";
-      ss_ << "| " << right << setw (13) << node_str << " ";
+      ss_ << "| " << right << setw (7) << cls << " ";
+      ss_ << "| " << right << setw (15) << node_str << " ";
       ss_ << "| " << right << setw (4) << tops_str << " ";
       ss_ << "| " << right << setw (8) << status_str << " ";
       ss_ << "| " << right << setw (12) << num_str << " |\n";
@@ -198,18 +295,150 @@ TrinitySMI::append_devices ()
     }
   }
   if (count == 0)
-    ss_ << "| " << left << setw (55) << "No available device detected" << "|\n";
-  ss_ << "+--------------------------------------------------------+\n";
+    ss_ << "| " << left << setw (59) << "No available device detected" << "|\n";
+  ss_ << "+------------------------------------------------------------+\n";
+}
+
+/** @brief show the list of executed apps */
+void
+TrinitySMI::append_apps ()
+{
+  ss_ << "\n";
+  ss_ << "+------------------------------------------------------------+\n";
+  ss_ << "| APP ID |    APP NAME     |   STATUS   | MEM (KiB) | # RUNS |\n";
+  ss_ << "|============================================================|\n";
+
+#ifndef ENABLE_EMUL
+  dev_type type;
+  int dev_id;
+
+  if (!parse_device_node (type, dev_id))
+    return;
+
+  uint32_t count = 0;
+  npudev_h dev;
+  npu_stat_apps stat_apps;
+
+  if (getNPUdeviceByType (&dev, type, dev_id) != 0)
+    goto out;
+
+  if (getNPU_statApps (dev, &stat_apps) != 0)
+    goto out_free;
+
+  for (uint32_t i = 0; i < stat_apps.num; i++) {
+    npu_stat_app *stat = &(stat_apps.stat[i]);
+    string status_str;
+
+    if (stat->status == NPU_APP_ERROR)
+      status_str = string ("error");
+    else if (stat->status == NPU_APP_PENDING)
+      status_str = string ("pending");
+    else if (stat->status == NPU_APP_STARTED)
+      status_str = string ("started");
+    else if (stat->status == NPU_APP_TERMINATED)
+      status_str = string ("terminated");
+    else
+      status_str = string ("unknown");
+
+    ss_ << "| " << right << setw(6) << stat->appid << " ";
+    ss_ << "| " << right << setw(15) << stat->name << " ";
+    ss_ << "| " << right << setw(10) << status_str << " ";
+    ss_ << "| " << right << setw(9) << fixed << setprecision(2);
+    ss_ << (((double) stat->total_alloc_mem) / 1024.0) << " ";
+    ss_ << "| " << right << setw(6) << stat->num_total_tasks << " |\n";
+    count++;
+  }
+
+  putNPU_statApps (&stat_apps);
+
+out_free:
+  putNPUdevice (dev);
+out:
+  if (count == 0)
+    ss_ << "| " << left << setw (59) << "No executed applications found" << "|\n";
+#else
+  ss_ << "| " << left << setw (59) << "Not supported envionment" << "|\n";
+#endif
+
+  ss_ << "+------------------------------------------------------------+\n";
 }
 
-/** @brief show the list of running processes */
+/** @brief show the list of submitted tasks */
 void
-TrinitySMI::append_processes ()
+TrinitySMI::append_tasks ()
 {
-  /** TODO */
-  ss_ << "+--------------------------------------------------------+\n";
-  ss_ << "| " << left << setw (55) << "No running processes found" << "|\n";
-  ss_ << "+--------------------------------------------------------+\n";
+  ss_ << "\n";
+  ss_ << "+------------------------------------------------------------+\n";
+  ss_ << "| TASK ID |  MODEL ID  | PRIO |  STATUS  |  SCHED  |  INFER  |\n";
+  ss_ << "|============================================================|\n";
+
+#ifndef ENABLE_EMUL
+  dev_type type;
+  int dev_id, app_id;
+
+  if (!parse_device_node (type, dev_id))
+    return;
+
+  if (!parse_app_id (app_id))
+    return;
+
+  uint32_t count = 0;
+  npudev_h dev;
+  npu_stat_tasks stat_tasks;
+
+  if (getNPUdeviceByType (&dev, type, dev_id) != 0)
+    goto out;
+
+  if (getNPU_statTasks (dev, app_id, &stat_tasks) != 0)
+    goto out_free;
+
+  for (uint32_t i = 0; i < stat_tasks.num; i++) {
+    npu_stat_task *stat = &(stat_tasks.stat[i]);
+    string priority_str;
+    string status_str;
+
+    if (stat->priority == NPU_PRIORITY_HIGH)
+      priority_str = string ("high");
+    else if (stat->priority == NPU_PRIORITY_MID)
+      priority_str = string ("mid");
+    else if (stat->priority == NPU_PRIORITY_LOW)
+      priority_str = string ("low");
+    else
+      priority_str = string ("unknown");
+
+    if (stat->status == NPU_TASK_ERROR)
+      status_str = string ("error");
+    else if (stat->status == NPU_TASK_PENDING)
+      status_str = string ("pending");
+    else if (stat->status == NPU_TASK_RUNNING)
+      status_str = string ("running");
+    else if (stat->status == NPU_TASK_FINISHED)
+      status_str = string ("finished");
+    else
+      status_str = string ("unknown");
+
+    ss_ << "| " << right << setw(7) << stat->taskid << " ";
+    ss_ << "| " << right << setw(10) << to_hex_string (stat->modelid) << " ";
+    ss_ << "| " << right << setw(4) << priority_str << " ";
+    ss_ << "| " << right << setw(8) << status_str << " ";
+    ss_ << "| " << right << setw(7) << to_string (stat->sched_time) + "ms" << " ";
+    ss_ << "| " << right << setw(7) << to_string (stat->infer_time) + "ms" << " |\n";
+    count++;
+  }
+
+  putNPU_statTasks (&stat_tasks);
+
+out_free:
+  putNPUdevice (dev);
+out:
+  if (count == 0)
+    ss_ << "| " << left << setw (59) << "No submitted tasks found" << "|\n";
+
+#else
+  ss_ << "| " << left << setw (59) << "Not supported envionment" << "|\n";
+#endif
+
+  ss_ << "+------------------------------------------------------------+\n";
 }
 
 /** @brief show the usage and help messages */
@@ -218,9 +447,50 @@ TrinitySMI::append_help ()
 {
   ss_ << "\nUsage: " << name_ << " [options]\n";
   ss_ << "Options:\n";
-  ss_ << "  -d \t\t Show list of trinity devices\n";
-  ss_ << "  -p \t\t Show list of running processes\n";
-  ss_ << "  -h \t\t Show help messages\n";
+  ss_ << "  -h \t\tShow help messages\n";
+  ss_ << "  -d [dev node] Specify a device node that you're interested in\n";
+  ss_ << "\t\t(e.g., -d triv2-0)\n";
+  ss_ << "  -a [app id] \tSpecify a app id that you're interested in\n";
+  ss_ << "\t\t(e.g., -d triv2-0 -a 125)\n";
+}
+
+/** @brief check the given arguments */
+bool
+TrinitySMI::check_arguments (int argc, char ** argv)
+{
+  int c;
+
+  optind = 0;
+  opterr = 0;
+  while ((c = getopt (argc, argv, "hd:a:")) != -1) {
+    switch (c) {
+      case 'd':
+        set_device (optarg);
+        break;
+      case 'a':
+        set_appid (optarg);
+        break;
+      case '?':
+        if (optopt == 'd')
+          ss_ << "\nOption '-d' requires an extra argument\n";
+        else if (optopt == 'a')
+          ss_ << "\nOption '-a' requires an extra argument\n";
+        else
+          ss_ << "\nUnknown flag detected: " << (char) optopt << "\n";
+        // no-break
+      case 'h':
+        append_help ();
+        return false;
+    }
+  }
+
+  if (!appid_.empty () && node_.empty ()) {
+    ss_ << "\nOption '-a' requires Option '-d' as well, to specify a device node\n";
+    append_help ();
+    return false;
+  }
+
+  return true;
 }
 
 /** @brief show the appended string to screen */
@@ -236,29 +506,9 @@ int main (int argc, char **argv)
   TrinitySMI smi (argv[0]);
 
   smi.append_version ();
-
-  if (argc > 1) {
-    int c;
-
-    optind = 0;
-    opterr = 0;
-    while ((c = getopt (argc, argv, "dph")) != -1) {
-      switch (c) {
-        case 'd':
-          smi.append_devices ();
-          break;
-        case 'p':
-          smi.append_processes ();
-          break;
-        case 'h':
-          smi.append_help ();
-          break;
-      }
-    }
-  } else {
-    smi.append_help ();
-  }
-
+  if (smi.check_arguments (argc, argv))
+    smi.append_stats ();
   smi.dump ();
+
   return 0;
 }