mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-01 20:29:00 +00:00
nshlib: Support top command
show thread sort by cpuloading: use `top [-n <num> ][ -d <delay>] [ -p <pidlist>] [ -h ]` Signed-off-by: anjiahao <anjiahao@xiaomi.com> Signed-off-by: yinshengkai <yinshengkai@xiaomi.com>
This commit is contained in:
parent
6951b64038
commit
6979fca4dc
4 changed files with 280 additions and 0 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue