This is an automated email from the ASF dual-hosted git repository.

ligd pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git

commit 6979fca4dcee72fb7c96f43ca5a0ab3675b1891b
Author: anjiahao <anjia...@xiaomi.com>
AuthorDate: Tue May 21 21:41:45 2024 +0800

    nshlib: Support top command
    
    show thread sort by cpuloading:
    use `top [-n <num> ][ -d <delay>] [ -p <pidlist>] [ -h ]`
    
    Signed-off-by: anjiahao <anjia...@xiaomi.com>
    Signed-off-by: yinshengkai <yinsheng...@xiaomi.com>
---
 nshlib/Kconfig        |   5 +
 nshlib/nsh.h          |   3 +
 nshlib/nsh_command.c  |   5 +
 nshlib/nsh_proccmds.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 280 insertions(+)

diff --git a/nshlib/Kconfig b/nshlib/Kconfig
index 72e117c0a..2492673f5 100644
--- a/nshlib/Kconfig
+++ b/nshlib/Kconfig
@@ -637,6 +637,11 @@ config NSH_DISABLE_TEST
        bool "Disable test"
        default DEFAULT_SMALL
 
+config NSH_DISABLE_TOP
+       bool "Disable top"
+       default DEFAULT_SMALL
+       depends on !NSH_DISABLE_PS
+
 config NSH_DISABLE_TRUNCATE
        bool "Disable truncate"
        default DEFAULT_SMALL
diff --git a/nshlib/nsh.h b/nshlib/nsh.h
index 35f2d20a3..204308368 100644
--- a/nshlib/nsh.h
+++ b/nshlib/nsh.h
@@ -929,6 +929,9 @@ void nsh_usbtrace(void);
 #ifndef CONFIG_NSH_DISABLE_TIME
   int cmd_time(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
 #endif
+#if !defined(CONFIG_NSH_DISABLE_TOP) && defined(NSH_HAVE_CPULOAD)
+  int cmd_top(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
+#endif
 #ifndef CONFIG_NSH_DISABLE_PS
   int cmd_ps(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
 #endif
diff --git a/nshlib/nsh_command.c b/nshlib/nsh_command.c
index 46d8c14cf..1cff20bd5 100644
--- a/nshlib/nsh_command.c
+++ b/nshlib/nsh_command.c
@@ -595,6 +595,11 @@ static const struct cmdmap_s g_cmdmap[] =
           3, CONFIG_NSH_MAXARGUMENTS, "<expression>"),
 #endif
 
+#if !defined(CONFIG_NSH_DISABLE_TOP) && defined(NSH_HAVE_CPULOAD)
+  CMD_MAP("top",       cmd_top,       1, 5,
+          "[ -n <num> ][ -d <delay>] [ -p <pidlist>] [-h]"),
+#endif
+
 #ifndef CONFIG_NSH_DISABLE_TIME
   CMD_MAP("time",     cmd_time,     2, 2, "\"<command>\""),
 #endif
diff --git a/nshlib/nsh_proccmds.c b/nshlib/nsh_proccmds.c
index 7a8a77fce..84ce4b2b2 100644
--- a/nshlib/nsh_proccmds.c
+++ b/nshlib/nsh_proccmds.c
@@ -33,6 +33,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <signal.h>
+#include <sys/ioctl.h>
 #include <sys/sysinfo.h>
 #include <sys/param.h>
 #include <time.h>
@@ -113,6 +114,14 @@ struct nsh_taskstatus_s
   size_t          td_bufpos;       /* Position in the buffer */
 };
 
+struct nsh_topstatus_s
+{
+  FAR struct nsh_taskstatus_s **status;
+  bool heap;
+  size_t size;
+  size_t index;
+};
+
 /* Status strings */
 
 #ifndef CONFIG_NSH_DISABLE_PS
@@ -668,6 +677,129 @@ static int ps_callback(FAR struct nsh_vtbl_s *vtbl, FAR 
const char *dirpath,
 }
 #endif
 
+#if !defined(CONFIG_NSH_DISABLE_TOP) && defined(NSH_HAVE_CPULOAD)
+
+/****************************************************************************
+ * Name: top_callback
+ ****************************************************************************/
+
+static int top_callback(FAR struct nsh_vtbl_s *vtbl, FAR const char *dirpath,
+                        FAR struct dirent *entryp, FAR void *pvarg)
+{
+  FAR struct nsh_topstatus_s *topstatus = pvarg;
+  FAR struct nsh_taskstatus_s *status;
+  int index = topstatus->index;
+  int ret;
+
+  if (ps_skipfile(entryp))
+    {
+      return OK;
+    }
+
+  if (topstatus->size == 0)
+    {
+      topstatus->status = zalloc(sizeof(FAR struct nsh_taskstatus_s *) * 4);
+      if (topstatus->status == NULL)
+        {
+          nsh_error(vtbl, g_fmtcmdfailed, "top", "zalloc", NSH_ERRNO);
+          return -ENOMEM;
+        }
+
+      topstatus->size = 4;
+    }
+  else if (topstatus->index >= topstatus->size)
+    {
+      topstatus->status =
+        realloc(topstatus->status,
+                sizeof(topstatus->status[0]) * topstatus->size * 2);
+      if (topstatus->status == NULL)
+        {
+          nsh_error(vtbl, g_fmtcmdfailed, "top", "realloc", NSH_ERRNO);
+          return -ENOMEM;
+        }
+
+      memset(&topstatus->status[topstatus->index], 0,
+             sizeof(topstatus->status[0]) * topstatus->size);
+      topstatus->size *= 2;
+    }
+
+  if (topstatus->status[index] != NULL)
+    {
+      status = topstatus->status[index];
+      status->td_bufpos = 0;
+    }
+  else
+    {
+      status = zalloc(sizeof(struct nsh_taskstatus_s));
+      if (status == NULL)
+        {
+          nsh_error(vtbl, g_fmtcmdfailed, "top", "zalloc", NSH_ERRNO);
+          return -ENOMEM;
+        }
+
+      topstatus->status[index] = status;
+      status->td_buf = zalloc(IOBUFFERSIZE);
+      if (status->td_buf == NULL)
+        {
+          nsh_error(vtbl, g_fmtcmdfailed, "top", "zalloc", NSH_ERRNO);
+          return -ENOMEM;
+        }
+
+      status->td_bufsize = IOBUFFERSIZE;
+    }
+
+  ret = ps_record(vtbl, dirpath, entryp, topstatus->heap, status);
+  if (ret < 0)
+    {
+      nsh_error(vtbl, g_fmtcmdfailed, "top", "ps_record", NSH_ERRNO);
+      return ret;
+    }
+
+  topstatus->index++;
+  return ret;
+}
+
+/****************************************************************************
+ * Name: top_cmpcpuload
+ ****************************************************************************/
+
+static int top_cmpcpuload(FAR const void *item1, FAR const void *item2)
+{
+  FAR const struct nsh_taskstatus_s *status1 =
+    (FAR const struct nsh_taskstatus_s *)(*(FAR uintptr_t *)item1);
+  FAR const struct nsh_taskstatus_s *status2 =
+    (FAR const struct nsh_taskstatus_s *)(*(FAR uintptr_t *)item2);
+  int load1 = atoi(status1->td_cpuload);
+  int load2 = atoi(status2->td_cpuload);
+  FAR const char *s1;
+  FAR const char *s2;
+
+  if (load1 == load2)
+    {
+      s1 = status1->td_cpuload;
+      s2 = status2->td_cpuload;
+      while (*s1++ != '.');
+      while (*s2++ != '.');
+
+      return *s2 > *s1;
+    }
+  else
+    {
+      return load2 > load1;
+    }
+}
+
+/****************************************************************************
+ * Name: top_exit
+ ****************************************************************************/
+
+static void top_exit(int signo, FAR siginfo_t *siginfo, FAR void *context)
+{
+  *(FAR bool *)siginfo->si_user = true;
+}
+
+#endif
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -1130,3 +1262,138 @@ int cmd_uptime(FAR struct nsh_vtbl_s *vtbl, int argc, 
FAR char **argv)
   return OK;
 }
 #endif
+
+/****************************************************************************
+ * Name: cmd_top
+ ****************************************************************************/
+
+#if !defined(CONFIG_NSH_DISABLE_TOP) && defined(NSH_HAVE_CPULOAD)
+int cmd_top(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
+{
+  FAR char *pidlist = NULL;
+  size_t num = SIZE_MAX;
+  size_t i;
+  struct sigaction act;
+  bool quit = false;
+  int delay = 3;
+  int ret = 0;
+  int tc = 0;
+  int opt;
+  struct nsh_topstatus_s topstatus =
+    {
+      0
+    };
+
+  while ((opt = getopt(argc, argv, "d:p:n:h")) != ERROR)
+    {
+      switch (opt)
+        {
+          case 'd':
+            delay = atoi(optarg);
+            break;
+          case 'p':
+            pidlist = optarg;
+            break;
+          case 'h':
+            topstatus.heap = true;
+            break;
+          case 'n':
+            num = atoi(optarg);
+            break;
+
+          default:
+            nsh_output(vtbl, "Usage: top"
+                             "[ -n <num>] [ -d <delay>]"
+                             "[ -p <pidlist>] [ -h ]\n");
+            return ERROR;
+        }
+    }
+
+  act.sa_user = &quit;
+  act.sa_sigaction = top_exit;
+  sigemptyset(&act.sa_mask);
+  act.sa_flags = 0;
+  if (sigaction(SIGINT, &act, NULL) < 0)
+    {
+      nsh_error(vtbl, g_fmtcmdfailed, "top", "sigaction", NSH_ERRNO);
+      return ERROR;
+    }
+
+  if (vtbl->isctty)
+    {
+      tc = nsh_ioctl(vtbl, TIOCSCTTY, getpid());
+    }
+
+  while (!quit)
+    {
+      topstatus.index = 0;
+      nsh_output(vtbl, "\033[2J\033[1;1H");
+      ps_title(vtbl, topstatus.heap);
+
+      if (pidlist)
+        {
+          FAR char *save = NULL;
+          FAR char *pid = strtok_r(pidlist, ",", &save);
+
+          while (pid != NULL && ret >= 0)
+            {
+              struct dirent entry;
+              entry.d_type = DT_DIR;
+              strlcpy(entry.d_name, pid, sizeof(entry.d_name));
+              ret = top_callback(vtbl, CONFIG_NSH_PROC_MOUNTPOINT,
+                                 &entry, &topstatus);
+              *--pid = ',';
+              pid = strtok_r(NULL, ",", &save);
+            }
+        }
+      else
+        {
+          ret = nsh_foreach_direntry(vtbl, "top", CONFIG_NSH_PROC_MOUNTPOINT,
+                                     top_callback, &topstatus);
+        }
+
+      if (ret < 0)
+        {
+          nsh_error(vtbl, g_fmtcmdfailed, "top", "nsh_foreach_direntry",
+                    NSH_ERRNO);
+          break;
+        }
+
+      qsort(topstatus.status, topstatus.index,
+            sizeof(topstatus.status[0]), top_cmpcpuload);
+
+      for (i = 0; i < MIN(topstatus.index, num); i++)
+        {
+          ps_output(vtbl, topstatus.heap, topstatus.status[i]);
+        }
+
+      if (vtbl->isctty && tc == 0)
+        {
+          nsh_output(vtbl, "use Ctrl+c' to quit\n");
+        }
+
+      sleep(delay);
+    }
+
+  if (topstatus.status != NULL)
+    {
+      for (i = 0; i < topstatus.size; i++)
+        {
+          if (topstatus.status[i] != NULL)
+            {
+              free(topstatus.status[i]->td_buf);
+              free(topstatus.status[i]);
+            }
+        }
+
+      free(topstatus.status);
+    }
+
+  if (vtbl->isctty && tc == 0)
+    {
+      nsh_ioctl(vtbl, TIOCNOTTY, 0);
+    }
+
+  return ret;
+}
+#endif

Reply via email to