aboutsummaryrefslogtreecommitdiff
path: root/procps/top.c
diff options
context:
space:
mode:
Diffstat (limited to 'procps/top.c')
-rw-r--r--procps/top.c435
1 files changed, 382 insertions, 53 deletions
diff --git a/procps/top.c b/procps/top.c
index 87e85003d..69f63d08b 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -58,11 +58,17 @@ typedef struct save_hist {
typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
+
enum { SORT_DEPTH = 3 };
+
struct globals {
top_status_t *top;
int ntop;
+#if ENABLE_FEATURE_TOPMEM
+ smallint sort_field;
+ smallint inverted;
+#endif
#if ENABLE_FEATURE_USE_TERMIOS
struct termios initial_settings;
#endif
@@ -81,7 +87,9 @@ struct globals {
#define G (*(struct globals*)&bb_common_bufsiz1)
#define top (G.top )
#define ntop (G.ntop )
-#define initial_settings (G. initial_settings )
+#define sort_field (G.sort_field )
+#define inverted (G.inverted )
+#define initial_settings (G.initial_settings )
#define sort_function (G.sort_function )
#define prev_hist (G.prev_hist )
#define prev_hist_count (G.prev_hist_count )
@@ -371,15 +379,11 @@ static void display_process_list(int count, int scr_width)
/* what info of the processes is shown */
printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,
" PID PPID USER STAT VSZ %MEM %CPU COMMAND");
-#define MIN_WIDTH \
- sizeof( " PID PPID USER STAT VSZ %MEM %CPU C")
#else
/* !CPU_USAGE_PERCENTAGE */
printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,
" PID PPID USER STAT VSZ %MEM COMMAND");
-#define MIN_WIDTH \
- sizeof( " PID PPID USER STAT VSZ %MEM C")
#endif
#if ENABLE_FEATURE_TOP_DECIMALS
@@ -434,20 +438,23 @@ static void display_process_list(int count, int scr_width)
/* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
#endif
- /* Ok, all prelim data is ready, go thru the list */
+ scr_width += 2; /* account for leading '\n' and trailing NUL */
+ /* Ok, all preliminary data is ready, go thru the list */
while (count-- > 0) {
- int col = scr_width;
+ char buf[scr_width];
+ unsigned col;
CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
#endif
- if (s->vsz >= 100*1024)
+ if (s->vsz >= 100000)
sprintf(vsz_str_buf, "%6ldm", s->vsz/1024);
else
sprintf(vsz_str_buf, "%7ld", s->vsz);
// PID PPID USER STAT VSZ %MEM [%CPU] COMMAND
- col -= printf("\n" "%5u%6u %-8.8s %s%s" FMT
+ col = snprintf(buf, scr_width,
+ "\n" "%5u%6u %-8.8s %s%s" FMT
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
FMT
#endif
@@ -459,11 +466,9 @@ static void display_process_list(int count, int scr_width)
, SHOW_STAT(pcpu)
#endif
);
- if (col > 0) {
- char buf[col + 1];
- read_cmdline(buf, col, s->pid, s->comm);
- fputs(buf, stdout);
- }
+ if (col < scr_width)
+ read_cmdline(buf + col, scr_width - col, s->pid, s->comm);
+ fputs(buf, stdout);
/* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
jif.busy - prev_jif.busy, jif.total - prev_jif.total); */
s++;
@@ -481,7 +486,7 @@ static void clearmems(void)
{
clear_username_cache();
free(top);
- top = 0;
+ top = NULL;
ntop = 0;
}
@@ -508,13 +513,298 @@ static void sig_catcher(int sig ATTRIBUTE_UNUSED)
#endif /* FEATURE_USE_TERMIOS */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+typedef unsigned long mem_t;
+
+typedef struct topmem_status_t {
+ unsigned pid;
+ char comm[COMM_LEN];
+ /* vsz doesn't count /dev/xxx mappings except /dev/zero */
+ mem_t vsz ;
+ mem_t vszrw ;
+ mem_t rss ;
+ mem_t rss_sh ;
+ mem_t dirty ;
+ mem_t dirty_sh;
+ mem_t stack ;
+} topmem_status_t;
+
+enum { NUM_SORT_FIELD = 7 };
+
+#define topmem ((topmem_status_t*)top)
+
+#if ENABLE_FEATURE_TOPMEM
+static int topmem_sort(char *a, char *b)
+{
+ int n;
+ mem_t l, r;
+
+ n = offsetof(topmem_status_t, vsz) + (sort_field * sizeof(mem_t));
+ l = *(mem_t*)(a + n);
+ r = *(mem_t*)(b + n);
+// if (l == r) {
+// l = a->mapped_rw;
+// r = b->mapped_rw;
+// }
+ /* We want to avoid unsigned->signed and truncation errors */
+ /* l>r: -1, l=r: 0, l<r: 1 */
+ n = (l > r) ? -1 : (l != r);
+ return inverted ? -n : n;
+}
+
+/* Cut "NNNN " out of " NNNN kb" */
+static char *grab_number(char *str, const char *match, unsigned sz)
+{
+ if (strncmp(str, match, sz) == 0) {
+ str = skip_whitespace(str + sz);
+ (skip_non_whitespace(str))[1] = '\0';
+ return xstrdup(str);
+ }
+ return NULL;
+}
+
+/* display header info (meminfo / loadavg) */
+static void display_topmem_header(int scr_width)
+{
+ char linebuf[128];
+ int i;
+ FILE *fp;
+ union {
+ struct {
+ /* 1 */ char *total;
+ /* 2 */ char *mfree;
+ /* 3 */ char *buf;
+ /* 4 */ char *cache;
+ /* 5 */ char *swaptotal;
+ /* 6 */ char *swapfree;
+ /* 7 */ char *dirty;
+ /* 8 */ char *mwrite;
+ /* 9 */ char *anon;
+ /* 10 */ char *map;
+ /* 11 */ char *slab;
+ };
+ char *str[11];
+ } Z;
+#define total Z.total
+#define mfree Z.mfree
+#define buf Z.buf
+#define cache Z.cache
+#define swaptotal Z.swaptotal
+#define swapfree Z.swapfree
+#define dirty Z.dirty
+#define mwrite Z.mwrite
+#define anon Z.anon
+#define map Z.map
+#define slab Z.slab
+#define str Z.str
+
+ memset(&Z, 0, sizeof(Z));
+
+ /* read memory info */
+ fp = xfopen("meminfo", "r");
+ while (fgets(linebuf, sizeof(linebuf), fp)) {
+ char *p;
+
+#define SCAN(match, name) \
+ p = grab_number(linebuf, match, sizeof(match)-1); \
+ if (p) { name = p; continue; }
+
+ SCAN("MemTotal:", total);
+ SCAN("MemFree:", mfree);
+ SCAN("Buffers:", buf);
+ SCAN("Cached:", cache);
+ SCAN("SwapTotal:", swaptotal);
+ SCAN("SwapFree:", swapfree);
+ SCAN("Dirty:", dirty);
+ SCAN("Writeback:", mwrite);
+ SCAN("AnonPages:", anon);
+ SCAN("Mapped:", map);
+ SCAN("Slab:", slab);
+#undef SCAN
+ }
+ fclose(fp);
+
+#define S(s) (s ? s : "0")
+ snprintf(linebuf, sizeof(linebuf),
+ "Mem %stotal %sanon %smap %sfree",
+ S(total), S(anon), S(map), S(mfree));
+ printf(OPT_BATCH_MODE ? "%.*s\n" : "\e[H\e[J%.*s\n", scr_width, linebuf);
+
+ snprintf(linebuf, sizeof(linebuf),
+ " %sslab %sbuf %scache %sdirty %swrite",
+ S(slab), S(buf), S(cache), S(dirty), S(mwrite));
+ printf("%.*s\n", scr_width, linebuf);
+
+ snprintf(linebuf, sizeof(linebuf),
+ "Swap %stotal %sfree", // TODO: % used?
+ S(swaptotal), S(swapfree));
+ printf("%.*s\n", scr_width, linebuf);
+#undef S
+
+ for (i = 0; i < ARRAY_SIZE(str); i++)
+ free(str[i]);
+#undef total
+#undef free
+#undef buf
+#undef cache
+#undef swaptotal
+#undef swapfree
+#undef dirty
+#undef write
+#undef anon
+#undef map
+#undef slab
+#undef str
+}
+
+// Converts unsigned long long value into compact 5-char
+// representation. Sixth char is always ' '
+static void smart_ulltoa6(unsigned long long ul, char buf[6])
+{
+ const char *fmt;
+ char c;
+ unsigned v, u, idx = 0;
+
+ if (ul > 99999) { // do not scale if 99999 or less
+ ul *= 10;
+ do {
+ ul /= 1024;
+ idx++;
+ } while (ul >= 100000);
+ }
+ v = ul; // ullong divisions are expensive, avoid them
+
+ fmt = " 123456789";
+ u = v / 10;
+ v = v % 10;
+ if (!idx) {
+ // 99999 or less: use "12345" format
+ // u is value/10, v is last digit
+ c = buf[0] = " 123456789"[u/1000];
+ if (c != ' ') fmt = "0123456789";
+ c = buf[1] = fmt[u/100%10];
+ if (c != ' ') fmt = "0123456789";
+ c = buf[2] = fmt[u/10%10];
+ if (c != ' ') fmt = "0123456789";
+ buf[3] = fmt[u%10];
+ buf[4] = "0123456789"[v];
+ } else {
+ // value has been scaled into 0..9999.9 range
+ // u is value, v is 1/10ths (allows for 92.1M format)
+ if (u >= 100) {
+ // value is >= 100: use "1234M', " 123M" formats
+ c = buf[0] = " 123456789"[u/1000];
+ if (c != ' ') fmt = "0123456789";
+ c = buf[1] = fmt[u/100%10];
+ if (c != ' ') fmt = "0123456789";
+ v = u % 10;
+ u = u / 10;
+ buf[2] = fmt[u%10];
+ } else {
+ // value is < 100: use "92.1M" format
+ c = buf[0] = " 123456789"[u/10];
+ if (c != ' ') fmt = "0123456789";
+ buf[1] = fmt[u%10];
+ buf[2] = '.';
+ }
+ buf[3] = "0123456789"[v];
+ // see http://en.wikipedia.org/wiki/Tera
+ buf[4] = " mgtpezy"[idx];
+ }
+ buf[5] = ' ';
+}
+
+static void display_topmem_process_list(int count, int scr_width)
+{
+#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK"
+#define MIN_WIDTH sizeof(HDR_STR)
+ const topmem_status_t *s = topmem;
+ char buf[scr_width | MIN_WIDTH]; /* a|b is a cheap max(a,b) */
+
+ display_topmem_header(scr_width);
+ strcpy(buf, HDR_STR " COMMAND");
+ buf[5 + sort_field * 6] = '*';
+ printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, buf);
+
+ while (--count >= 0) {
+ // PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND
+ smart_ulltoa6(s->pid , &buf[0*6]);
+ smart_ulltoa6(s->vsz , &buf[1*6]);
+ smart_ulltoa6(s->vszrw , &buf[2*6]);
+ smart_ulltoa6(s->rss , &buf[3*6]);
+ smart_ulltoa6(s->rss_sh , &buf[4*6]);
+ smart_ulltoa6(s->dirty , &buf[5*6]);
+ smart_ulltoa6(s->dirty_sh, &buf[6*6]);
+ smart_ulltoa6(s->stack , &buf[7*6]);
+ buf[8*6] = '\0';
+ if (scr_width > MIN_WIDTH) {
+ read_cmdline(&buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
+ }
+ printf("\n""%.*s", scr_width, buf);
+ s++;
+ }
+ putchar(OPT_BATCH_MODE ? '\n' : '\r');
+ fflush(stdout);
+#undef HDR_STR
+#undef MIN_WIDTH
+}
+#else
+void display_topmem_process_list(int count, int scr_width);
+int topmem_sort(char *a, char *b);
+#endif /* TOPMEM */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+enum {
+ TOP_MASK = 0
+ | PSSCAN_PID
+ | PSSCAN_PPID
+ | PSSCAN_VSZ
+ | PSSCAN_STIME
+ | PSSCAN_UTIME
+ | PSSCAN_STATE
+ | PSSCAN_COMM
+ | PSSCAN_UIDGID,
+ TOPMEM_MASK = 0
+ | PSSCAN_PID
+ | PSSCAN_SMAPS
+ | PSSCAN_COMM,
+};
+
int top_main(int argc, char **argv);
int top_main(int argc, char **argv)
{
int count, lines, col;
unsigned interval;
- int iterations = -1; /* infinite */
+ int iterations = 0; /* infinite */
char *sinterval, *siterations;
+ SKIP_FEATURE_TOPMEM(const) unsigned scan_mask = TOP_MASK;
#if ENABLE_FEATURE_USE_TERMIOS
struct termios new_settings;
struct pollfd pfd[1];
@@ -563,62 +853,82 @@ int top_main(int argc, char **argv)
procps_status_t *p = NULL;
/* Default */
- lines = 24 - 3 USE_FEATURE_TOP_CPU_GLOBAL_PERCENTS( - 1);
+ lines = 24;
col = 79;
#if ENABLE_FEATURE_USE_TERMIOS
get_terminal_width_height(0, &col, &lines);
- /* We wrap horribly if width is too narrow (TODO) */
- if (lines < 5 || col < MIN_WIDTH) {
+ if (lines < 5 || col < 10) {
sleep(interval);
continue;
}
- lines -= 3 USE_FEATURE_TOP_CPU_GLOBAL_PERCENTS( + 1);
#endif /* FEATURE_USE_TERMIOS */
+ if (!ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && scan_mask == TOP_MASK)
+ lines -= 3;
+ else
+ lines -= 4;
/* read process IDs & status for all the processes */
- while ((p = procps_scan(p, 0
- | PSSCAN_PID
- | PSSCAN_PPID
- | PSSCAN_VSZ
- | PSSCAN_STIME
- | PSSCAN_UTIME
- | PSSCAN_STATE
- | PSSCAN_COMM
- | PSSCAN_UIDGID
- )) != NULL) {
- int n = ntop;
- top = xrealloc(top, (++ntop) * sizeof(*top));
- top[n].pid = p->pid;
- top[n].ppid = p->ppid;
- top[n].vsz = p->vsz;
+ while ((p = procps_scan(p, scan_mask)) != NULL) {
+ int n;
+ if (scan_mask == TOP_MASK) {
+ n = ntop;
+ top = xrealloc(top, (++ntop) * sizeof(*top));
+ top[n].pid = p->pid;
+ top[n].ppid = p->ppid;
+ top[n].vsz = p->vsz;
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
- top[n].ticks = p->stime + p->utime;
+ top[n].ticks = p->stime + p->utime;
#endif
- top[n].uid = p->uid;
- strcpy(top[n].state, p->state);
- strcpy(top[n].comm, p->comm);
+ top[n].uid = p->uid;
+ strcpy(top[n].state, p->state);
+ strcpy(top[n].comm, p->comm);
+ } else { /* TOPMEM */
+#if ENABLE_FEATURE_TOPMEM
+ if (!(p->mapped_ro | p->mapped_rw))
+ continue; /* kernel threads are ignored */
+ n = ntop;
+ top = xrealloc(topmem, (++ntop) * sizeof(*topmem));
+ strcpy(topmem[n].comm, p->comm);
+ topmem[n].pid = p->pid;
+ topmem[n].vsz = p->mapped_rw + p->mapped_ro;
+ topmem[n].vszrw = p->mapped_rw;
+ topmem[n].rss_sh = p->shared_clean + p->shared_dirty;
+ topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh;
+ topmem[n].dirty = p->private_dirty + p->shared_dirty;
+ topmem[n].dirty_sh = p->shared_dirty;
+ topmem[n].stack = p->stack;
+#endif
+ }
}
if (ntop == 0) {
bb_error_msg_and_die("no process info in /proc");
}
+
+ if (scan_mask == TOP_MASK) {
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
- if (!prev_hist_count) {
+ if (!prev_hist_count) {
+ do_stats();
+ usleep(100000);
+ clearmems();
+ continue;
+ }
do_stats();
- sleep(1);
- clearmems();
- continue;
- }
- do_stats();
/* TODO: we don't need to sort all 10000 processes, we need to find top 24! */
- qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
+ qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
#else
- qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
+ qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
+ } else { /* TOPMEM */
+ qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
+ }
count = lines;
if (OPT_BATCH_MODE || count > ntop) {
count = ntop;
}
- display_process_list(count, col);
+ if (scan_mask == TOP_MASK)
+ display_process_list(count, col);
+ else
+ display_topmem_process_list(count, col);
clearmems();
if (iterations >= 0 && !--iterations)
break;
@@ -628,11 +938,17 @@ int top_main(int argc, char **argv)
if (poll(pfd, 1, interval * 1000) != 0) {
if (read(0, &c, 1) != 1) /* signal */
break;
- if (c == 'q' || c == initial_settings.c_cc[VINTR])
+ if (c == initial_settings.c_cc[VINTR])
+ break;
+ c |= 0x20; /* lowercase */
+ if (c == 'q')
break;
- if (c == 'N')
+ if (c == 'n') {
+ USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
sort_function[0] = pid_sort;
- if (c == 'M') {
+ }
+ if (c == 'm') {
+ USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
sort_function[0] = mem_sort;
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
sort_function[1] = pcpu_sort;
@@ -640,16 +956,29 @@ int top_main(int argc, char **argv)
#endif
}
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
- if (c == 'P') {
+ if (c == 'p') {
+ USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
sort_function[0] = pcpu_sort;
sort_function[1] = mem_sort;
sort_function[2] = time_sort;
}
- if (c == 'T') {
+ if (c == 't') {
+ USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
sort_function[0] = time_sort;
sort_function[1] = mem_sort;
sort_function[2] = pcpu_sort;
}
+#if ENABLE_FEATURE_TOPMEM
+ if (c == 's') {
+ scan_mask = TOPMEM_MASK;
+ free(prev_hist);
+ prev_hist = NULL;
+ prev_hist_count = 0;
+ sort_field = (sort_field + 1) % NUM_SORT_FIELD;
+ }
+ if (c == 'r')
+ inverted ^= 1;
+#endif
#endif
}
#endif /* FEATURE_USE_TERMIOS */