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