aboutsummaryrefslogtreecommitdiff
path: root/procps
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--procps/pmap.c141
-rw-r--r--procps/top.c355
2 files changed, 333 insertions, 163 deletions
diff --git a/procps/pmap.c b/procps/pmap.c
index 49f7688d9..0cac265c8 100644
--- a/procps/pmap.c
+++ b/procps/pmap.c
@@ -29,10 +29,14 @@
29 29
30#if ULLONG_MAX == 0xffffffff 30#if ULLONG_MAX == 0xffffffff
31# define TABS "\t" 31# define TABS "\t"
32# define SIZEWIDTHx "7"
33# define SIZEWIDTH "9"
32# define AFMTLL "8" 34# define AFMTLL "8"
33# define DASHES "" 35# define DASHES ""
34#else 36#else
35# define TABS "\t\t" 37# define TABS "\t\t"
38# define SIZEWIDTHx "15"
39# define SIZEWIDTH "17"
36# define AFMTLL "16" 40# define AFMTLL "16"
37# define DASHES "--------" 41# define DASHES "--------"
38#endif 42#endif
@@ -42,49 +46,160 @@ enum {
42 OPT_q = 1 << 1, 46 OPT_q = 1 << 1,
43}; 47};
44 48
45static void print_smaprec(struct smaprec *currec, void *data) 49struct smaprec {
46{ 50 // For mixed 32/64 userspace, 32-bit pmap still needs
47 unsigned opt = (uintptr_t)data; 51 // 64-bit field here to correctly show 64-bit processes:
52 unsigned long long smap_start;
53 // Make size wider too:
54 // I've seen 1203765248 kb large "---p" mapping in a browser,
55 // this cuts close to 4 terabytes.
56 unsigned long long smap_size;
57 // (strictly speaking, other fields need to be wider too,
58 // but they are in kbytes, not bytes, and they hold sizes,
59 // not start addresses, sizes tend to be less than 4 terabytes)
60 unsigned long private_dirty;
61 unsigned long smap_pss, smap_swap;
62 char smap_mode[5];
63 char *smap_name;
64};
48 65
66// How long the filenames and command lines we want to handle?
67#define PMAP_BUFSZ 4096
68
69static void print_smaprec(struct smaprec *currec)
70{
49 printf("%0" AFMTLL "llx ", currec->smap_start); 71 printf("%0" AFMTLL "llx ", currec->smap_start);
50 72
51 if (opt & OPT_x) 73 if (option_mask32 & OPT_x)
52 printf("%7lu %7lu %7lu %7lu ", 74 printf("%7llu %7lu %7lu %7lu ",
53 currec->smap_size, 75 currec->smap_size,
54 currec->smap_pss, 76 currec->smap_pss,
55 currec->private_dirty, 77 currec->private_dirty,
56 currec->smap_swap); 78 currec->smap_swap);
57 else 79 else
58 printf("%7luK", currec->smap_size); 80 printf("%7lluK", currec->smap_size);
81
82 printf(" %.4s %s\n", currec->smap_mode, currec->smap_name ? : " [ anon ]");
83}
84
85/* libbb's procps_read_smaps() looks somewhat similar,
86 * but the collected information is sufficiently different
87 * that merging them into one function is not a good idea
88 * (unless you feel masochistic today).
89 */
90static int read_smaps(pid_t pid, char buf[PMAP_BUFSZ], struct smaprec *total)
91{
92 FILE *file;
93 struct smaprec currec;
94 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
95
96 sprintf(filename, "/proc/%u/smaps", (int)pid);
97
98 file = fopen_for_read(filename);
99 if (!file)
100 return 1;
101
102 memset(&currec, 0, sizeof(currec));
103 while (fgets(buf, PMAP_BUFSZ, file)) {
104 // Each mapping datum has this form:
105 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
106 // Size: nnn kB
107 // Rss: nnn kB
108 // .....
109
110 char *tp, *p;
111
112#define bytes4 *(uint32_t*)buf
113#define Pss_ PACK32_BYTES('P','s','s',':')
114#define Swap PACK32_BYTES('S','w','a','p')
115#define Priv PACK32_BYTES('P','r','i','v')
116#define FETCH(X, N) \
117 tp = skip_whitespace(buf+4 + (N)); \
118 total->X += currec.X = fast_strtoul_10(&tp); \
119 continue
120#define SCAN(S, X) \
121if (memcmp(buf+4, S, sizeof(S)-1) == 0) { \
122 FETCH(X, sizeof(S)-1); \
123}
124 if (bytes4 == Pss_) {
125 FETCH(smap_pss, 0);
126 }
127 if (bytes4 == Swap && buf[4] == ':') {
128 FETCH(smap_swap, 1);
129 }
130 if (bytes4 == Priv) {
131 SCAN("ate_Dirty:", private_dirty);
132 }
133#undef bytes4
134#undef Pss_
135#undef Swap
136#undef Priv
137#undef FETCH
138#undef SCAN
139 tp = strchr(buf, '-');
140 if (tp) {
141 // We reached next mapping - the line of this form:
142 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
143
144 // If we have a previous record, there's nothing more
145 // for it, print and clear currec
146 if (currec.smap_size)
147 print_smaprec(&currec);
148 free(currec.smap_name);
149 memset(&currec, 0, sizeof(currec));
150
151 *tp = ' ';
152 tp = buf;
153 currec.smap_start = fast_strtoull_16(&tp);
154 currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10;
155 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1);
156
157 // skipping "rw-s FILEOFS M:m INODE "
158 tp = skip_fields(tp, 4);
159 tp = skip_whitespace(tp); // there may be many spaces, can't just "tp++"
160 p = strchrnul(tp, '\n');
161 if (p != tp) {
162 currec.smap_name = xstrndup(tp, p - tp);
163 }
164 total->smap_size += currec.smap_size;
165 }
166 } // while (got line)
167 fclose(file);
168
169 if (currec.smap_size)
170 print_smaprec(&currec);
171 free(currec.smap_name);
59 172
60 printf(" %.4s %s\n", currec->smap_mode, currec->smap_name); 173 return 0;
61} 174}
62 175
63static int procps_get_maps(pid_t pid, unsigned opt) 176static int procps_get_maps(pid_t pid, unsigned opt)
64{ 177{
65 struct smaprec total; 178 struct smaprec total;
66 int ret; 179 int ret;
67 char buf[256]; 180 char buf[PMAP_BUFSZ] ALIGN4;
181
182 ret = read_cmdline(buf, sizeof(buf), pid, NULL);
183 if (ret < 0)
184 return ret;
68 185
69 read_cmdline(buf, sizeof(buf), pid, NULL);
70 printf("%u: %s\n", (int)pid, buf); 186 printf("%u: %s\n", (int)pid, buf);
71 187
72 if (!(opt & OPT_q) && (opt & OPT_x)) 188 if (!(opt & OPT_q) && (opt & OPT_x))
73 puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping"); 189 puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping");
74 190
75 memset(&total, 0, sizeof(total)); 191 memset(&total, 0, sizeof(total));
76 192 ret = read_smaps(pid, buf, &total);
77 ret = procps_read_smaps(pid, &total, print_smaprec, (void*)(uintptr_t)opt);
78 if (ret) 193 if (ret)
79 return ret; 194 return ret;
80 195
81 if (!(opt & OPT_q)) { 196 if (!(opt & OPT_q)) {
82 if (opt & OPT_x) 197 if (opt & OPT_x)
83 printf("--------" DASHES " ------ ------ ------ ------\n" 198 printf("--------" DASHES " ------ ------ ------ ------\n"
84 "total" TABS " %7lu %7lu %7lu %7lu\n", 199 "total kB %"SIZEWIDTHx"llu %7lu %7lu %7lu\n",
85 total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); 200 total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap);
86 else 201 else
87 printf("mapped: %luK\n", total.smap_size); 202 printf(" total %"SIZEWIDTH"lluK\n", total.smap_size);
88 } 203 }
89 204
90 return 0; 205 return 0;
diff --git a/procps/top.c b/procps/top.c
index 09d31c673..7902e82f0 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -117,10 +117,15 @@
117 117
118#include "libbb.h" 118#include "libbb.h"
119 119
120#define ESC "\033" 120#define ESC "\033"
121#define HOME ESC"[H"
122#define CLREOS ESC"[J"
123#define CLREOL ESC"[K"
124#define REVERSE ESC"[7m"
125#define NORMAL ESC"[m"
121 126
122typedef struct top_status_t { 127typedef struct top_status_t {
123 unsigned long vsz; 128 unsigned long memsize;
124#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 129#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
125 unsigned long ticks; 130 unsigned long ticks;
126 unsigned pcpu; /* delta of ticks */ 131 unsigned pcpu; /* delta of ticks */
@@ -161,13 +166,16 @@ struct globals {
161 top_status_t *top; 166 top_status_t *top;
162 int ntop; 167 int ntop;
163 smallint inverted; 168 smallint inverted;
169 smallint first_line_printed;
164#if ENABLE_FEATURE_TOPMEM 170#if ENABLE_FEATURE_TOPMEM
165 smallint sort_field; 171 smallint sort_field;
166#endif 172#endif
167#if ENABLE_FEATURE_TOP_SMP_CPU 173#if ENABLE_FEATURE_TOP_SMP_CPU
168 smallint smp_cpu_info; /* one/many cpu info lines? */ 174 smallint smp_cpu_info; /* one/many cpu info lines? */
169#endif 175#endif
170 unsigned lines; /* screen height */ 176 int lines_remaining;
177 unsigned lines; /* screen height */
178 unsigned scr_width; /* width, clamped <= LINE_BUF_SIZE-2 */
171#if ENABLE_FEATURE_TOP_INTERACTIVE 179#if ENABLE_FEATURE_TOP_INTERACTIVE
172 struct termios initial_settings; 180 struct termios initial_settings;
173 int scroll_ofs; 181 int scroll_ofs;
@@ -212,7 +220,6 @@ struct globals {
212#define cpu_prev_jif (G.cpu_prev_jif ) 220#define cpu_prev_jif (G.cpu_prev_jif )
213#define num_cpus (G.num_cpus ) 221#define num_cpus (G.num_cpus )
214#define total_pcpu (G.total_pcpu ) 222#define total_pcpu (G.total_pcpu )
215#define line_buf (G.line_buf )
216#define INIT_G() do { \ 223#define INIT_G() do { \
217 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 224 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
218 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \ 225 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \
@@ -241,8 +248,8 @@ static int pid_sort(top_status_t *P, top_status_t *Q)
241static int mem_sort(top_status_t *P, top_status_t *Q) 248static int mem_sort(top_status_t *P, top_status_t *Q)
242{ 249{
243 /* We want to avoid unsigned->signed and truncation errors */ 250 /* We want to avoid unsigned->signed and truncation errors */
244 if (Q->vsz < P->vsz) return -1; 251 if (Q->memsize < P->memsize) return -1;
245 return Q->vsz != P->vsz; /* 0 if ==, 1 if > */ 252 return Q->memsize != P->memsize; /* 0 if ==, 1 if > */
246} 253}
247 254
248 255
@@ -283,9 +290,9 @@ static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
283#endif 290#endif
284 int ret; 291 int ret;
285 292
286 if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */) 293 if (!fgets(G.line_buf, LINE_BUF_SIZE, fp) || G.line_buf[0] != 'c' /* not "cpu" */)
287 return 0; 294 return 0;
288 ret = sscanf(line_buf, fmt, 295 ret = sscanf(G.line_buf, fmt,
289 &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle, 296 &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle,
290 &p_jif->iowait, &p_jif->irq, &p_jif->softirq, 297 &p_jif->iowait, &p_jif->irq, &p_jif->softirq,
291 &p_jif->steal); 298 &p_jif->steal);
@@ -362,7 +369,7 @@ static void do_stats(void)
362 369
363 get_jiffy_counts(); 370 get_jiffy_counts();
364 total_pcpu = 0; 371 total_pcpu = 0;
365 /* total_vsz = 0; */ 372 /* total_memsize = 0; */
366 new_hist = xmalloc(sizeof(new_hist[0]) * ntop); 373 new_hist = xmalloc(sizeof(new_hist[0]) * ntop);
367 /* 374 /*
368 * Make a pass through the data to get stats. 375 * Make a pass through the data to get stats.
@@ -394,7 +401,7 @@ static void do_stats(void)
394 i = (i+1) % prev_hist_count; 401 i = (i+1) % prev_hist_count;
395 /* hist_iterations++; */ 402 /* hist_iterations++; */
396 } while (i != last_i); 403 } while (i != last_i);
397 /* total_vsz += cur->vsz; */ 404 /* total_memsize += cur->memsize; */
398 } 405 }
399 406
400 /* 407 /*
@@ -407,6 +414,39 @@ static void do_stats(void)
407 414
408#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ 415#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
409 416
417static void print_line_buf(void)
418{
419 const char *fmt;
420
421 G.lines_remaining--;
422 fmt = OPT_BATCH_MODE ? "\n""%.*s" : "\n""%.*s"CLREOL;
423 if (!G.first_line_printed) {
424 G.first_line_printed = 1;
425 /* Go to top */
426 fmt = OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL;
427 }
428 printf(fmt, G.scr_width - 1, G.line_buf);
429}
430
431static void print_line_bold(void)
432{
433 G.lines_remaining--;
434//we never print first line in bold
435// if (!G.first_line_printed) {
436// printf(OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL, G.scr_width - 1, G.line_buf);
437// G.first_line_printed = 1;
438// } else {
439 printf(OPT_BATCH_MODE ? "\n""%.*s" : "\n"REVERSE"%.*s"NORMAL CLREOL, G.scr_width - 1, G.line_buf);
440// }
441}
442
443static void print_end(void)
444{
445 fputs_stdout(OPT_BATCH_MODE ? "\n" : CLREOS"\r");
446 /* next print will be "first line" (will clear the screen) */
447 G.first_line_printed = 0;
448}
449
410#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS 450#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
411/* formats 7 char string (8 with terminating NUL) */ 451/* formats 7 char string (8 with terminating NUL) */
412static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) 452static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
@@ -433,7 +473,7 @@ static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
433#endif 473#endif
434 474
435#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS 475#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
436static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) 476static void display_cpus(void)
437{ 477{
438 /* 478 /*
439 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100% 479 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100%
@@ -469,8 +509,8 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
469# else 509# else
470 /* Loop thru CPU(s) */ 510 /* Loop thru CPU(s) */
471 n_cpu_lines = smp_cpu_info ? num_cpus : 1; 511 n_cpu_lines = smp_cpu_info ? num_cpus : 1;
472 if (n_cpu_lines > *lines_rem_p) 512 if (n_cpu_lines > G.lines_remaining)
473 n_cpu_lines = *lines_rem_p; 513 n_cpu_lines = G.lines_remaining;
474 514
475 for (i = 0; i < n_cpu_lines; i++) { 515 for (i = 0; i < n_cpu_lines; i++) {
476 p_jif = &cpu_jif[i]; 516 p_jif = &cpu_jif[i];
@@ -488,7 +528,7 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
488 CALC_STAT(softirq); 528 CALC_STAT(softirq);
489 /*CALC_STAT(steal);*/ 529 /*CALC_STAT(steal);*/
490 530
491 snprintf(scrbuf, scr_width, 531 sprintf(G.line_buf,
492 /* Barely fits in 79 chars when in "decimals" mode. */ 532 /* Barely fits in 79 chars when in "decimals" mode. */
493# if ENABLE_FEATURE_TOP_SMP_CPU 533# if ENABLE_FEATURE_TOP_SMP_CPU
494 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", 534 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
@@ -501,16 +541,15 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
501 /*, SHOW_STAT(steal) - what is this 'steal' thing? */ 541 /*, SHOW_STAT(steal) - what is this 'steal' thing? */
502 /* I doubt anyone wants to know it */ 542 /* I doubt anyone wants to know it */
503 ); 543 );
504 puts(scrbuf); 544 print_line_buf();
505 } 545 }
506 } 546 }
507# undef SHOW_STAT 547# undef SHOW_STAT
508# undef CALC_STAT 548# undef CALC_STAT
509# undef FMT 549# undef FMT
510 *lines_rem_p -= i;
511} 550}
512#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */ 551#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */
513# define display_cpus(scr_width, scrbuf, lines_rem) ((void)0) 552# define display_cpus() ((void)0)
514#endif 553#endif
515 554
516enum { 555enum {
@@ -564,52 +603,55 @@ static void parse_meminfo(unsigned long meminfo[MI_MAX])
564 fclose(f); 603 fclose(f);
565} 604}
566 605
567static unsigned long display_header(int scr_width, int *lines_rem_p) 606static void cmdline_to_line_buf_and_print(unsigned offset, unsigned pid, const char *comm)
607{
608 int width = G.scr_width - offset;
609 if (width > 1) /* wider than to fit just the NUL? */
610 read_cmdline(G.line_buf + offset, width, pid, comm);
611//TODO: read_cmdline() sanitizes control chars, but not chars above 0x7e
612 print_line_buf();
613}
614
615static unsigned long display_header(void)
568{ 616{
569 char scrbuf[100]; /* [80] was a bit too low on 8Gb ram box */
570 char *buf; 617 char *buf;
571 unsigned long meminfo[MI_MAX]; 618 unsigned long meminfo[MI_MAX];
572 619
573 parse_meminfo(meminfo); 620 parse_meminfo(meminfo);
574 621
575 /* Output memory info */ 622 /* Output memory info */
576 if (scr_width > (int)sizeof(scrbuf)) 623 sprintf(G.line_buf,
577 scr_width = sizeof(scrbuf);
578 snprintf(scrbuf, scr_width,
579 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", 624 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
580 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE], 625 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE],
581 meminfo[MI_MEMFREE], 626 meminfo[MI_MEMFREE],
582 meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM], 627 meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM],
583 meminfo[MI_BUFFERS], 628 meminfo[MI_BUFFERS],
584 meminfo[MI_CACHED]); 629 meminfo[MI_CACHED]);
585 /* Go to top & clear to the end of screen */ 630 print_line_buf();
586 printf(OPT_BATCH_MODE ? "%s\n" : ESC"[H" ESC"[J" "%s\n", scrbuf);
587 (*lines_rem_p)--;
588 631
589 /* Display CPU time split as percentage of total time. 632 /* Display CPU time split as percentage of total time.
590 * This displays either a cumulative line or one line per CPU. 633 * This displays either a cumulative line or one line per CPU.
591 */ 634 */
592 display_cpus(scr_width, scrbuf, lines_rem_p); 635 display_cpus();
593 636
594 /* Read load average as a string */ 637 /* Read load average as a string */
595 buf = stpcpy(scrbuf, "Load average: "); 638 buf = stpcpy(G.line_buf, "Load average: ");
596 open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: ")); 639 open_read_close("loadavg", buf, sizeof(G.line_buf) - sizeof("Load average: "));
597 scrbuf[scr_width - 1] = '\0'; 640 G.line_buf[sizeof(G.line_buf) - 1] = '\0'; /* paranoia */
598 strchrnul(buf, '\n')[0] = '\0'; 641 strchrnul(buf, '\n')[0] = '\0';
599 puts(scrbuf); 642 print_line_buf();
600 (*lines_rem_p)--;
601 643
602 return meminfo[MI_MEMTOTAL]; 644 return meminfo[MI_MEMTOTAL];
603} 645}
604 646
605static NOINLINE void display_process_list(int lines_rem, int scr_width) 647static NOINLINE void display_process_list(void)
606{ 648{
607 enum { 649 enum {
608 BITS_PER_INT = sizeof(int) * 8 650 BITS_PER_INT = sizeof(int) * 8
609 }; 651 };
610 652
611 top_status_t *s; 653 top_status_t *s;
612 unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */ 654 unsigned long total_memory = display_header();
613 /* xxx_shift and xxx_scale variables allow us to replace 655 /* xxx_shift and xxx_scale variables allow us to replace
614 * expensive divides with multiply and shift */ 656 * expensive divides with multiply and shift */
615 unsigned pmem_shift, pmem_scale, pmem_half; 657 unsigned pmem_shift, pmem_scale, pmem_half;
@@ -621,7 +663,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
621 663
622#if ENABLE_FEATURE_TOP_DECIMALS 664#if ENABLE_FEATURE_TOP_DECIMALS
623# define UPSCALE 1000 665# define UPSCALE 1000
624typedef struct { unsigned quot, rem; } bb_div_t; 666 typedef struct { unsigned quot, rem; } bb_div_t;
625/* Used to have "div_t name = div((val), 10)" here 667/* Used to have "div_t name = div((val), 10)" here
626 * (IOW: intended to use libc-compatible way to divide and use 668 * (IOW: intended to use libc-compatible way to divide and use
627 * both result and remainder, but musl does not inline div()...) 669 * both result and remainder, but musl does not inline div()...)
@@ -629,28 +671,34 @@ typedef struct { unsigned quot, rem; } bb_div_t;
629 */ 671 */
630# define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 } 672# define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 }
631# define SHOW_STAT(name) name.quot, '0'+name.rem 673# define SHOW_STAT(name) name.quot, '0'+name.rem
674# define SANITIZE(name) if (name.quot > 99) name.quot = 99, name.rem = (unsigned char)('+' - '0')
632# define FMT "%3u.%c" 675# define FMT "%3u.%c"
633#else 676#else
634# define UPSCALE 100 677# define UPSCALE 100
635# define CALC_STAT(name, val) unsigned name = (val) 678# define CALC_STAT(name, val) unsigned name = (val)
679# define SANITIZE(name) if (name > 99) name = 99
636# define SHOW_STAT(name) name 680# define SHOW_STAT(name) name
637# define FMT "%4u%%" 681# define FMT "%4u%%"
638#endif 682#endif
639 683
640 /* what info of the processes is shown */ 684 strcpy(G.line_buf, " PID PPID USER STAT RSS %RSS"
641 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width,
642 " PID PPID USER STAT VSZ %VSZ"
643 IF_FEATURE_TOP_SMP_PROCESS(" CPU") 685 IF_FEATURE_TOP_SMP_PROCESS(" CPU")
644 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU") 686 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
645 " COMMAND"); 687 " COMMAND");
646 lines_rem--; 688 print_line_bold();
647 689
648 /* 690 /* %RSS = s->memsize / MemTotal * 100%
649 * %VSZ = s->vsz/MemTotal 691 * Calculate this with multiply and shift. Example:
692 * shift = 12
693 * scale = 100 * 0x1000 / total_memory
694 * percent_mem = (size_mem * scale) >> shift
695 * ~= (size_mem >> shift) * scale
696 * ~= (size_mem >> shift) * 100 * (1 << shift) / total_memory
697 * ~= size_mem * 100 / total_memory
650 */ 698 */
651 pmem_shift = BITS_PER_INT-11; 699 pmem_shift = BITS_PER_INT-11;
652 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory; 700 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
653 /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */ 701 /* s->memsize is in kb. we want (s->memsize * pmem_scale) to never overflow */
654 while (pmem_scale >= 512) { 702 while (pmem_scale >= 512) {
655 pmem_scale /= 4; 703 pmem_scale /= 4;
656 pmem_shift -= 2; 704 pmem_shift -= 2;
@@ -689,25 +737,29 @@ typedef struct { unsigned quot, rem; } bb_div_t;
689 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2); 737 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
690 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ 738 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
691#endif 739#endif
740 if (G.lines_remaining > ntop - G_scroll_ofs)
741 G.lines_remaining = ntop - G_scroll_ofs;
692 742
693 /* Ok, all preliminary data is ready, go through the list */ 743 /* Ok, all preliminary data is ready, go through the list */
694 scr_width += 2; /* account for leading '\n' and trailing NUL */
695 if (lines_rem > ntop - G_scroll_ofs)
696 lines_rem = ntop - G_scroll_ofs;
697 s = top + G_scroll_ofs; 744 s = top + G_scroll_ofs;
698 while (--lines_rem >= 0) { 745 while (G.lines_remaining > 0) {
699 int n; 746 int n;
700 char *ppu; 747 char *ppu;
701 char ppubuf[sizeof(int)*3 * 2 + 12]; 748 char ppubuf[sizeof(int)*3 * 2 + 12];
702 char vsz_str_buf[8]; 749 char memsize_str_buf[8];
703 unsigned col; 750 unsigned col;
704 751
705 CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift); 752 CALC_STAT(pmem, (s->memsize*pmem_scale + pmem_half) >> pmem_shift);
706#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 753#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
707 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift); 754 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
708#endif 755#endif
756 /* VSZ can be much larger than total memory
757 * (seen values close to 2Tbyte), don't try to display
758 * "uses 12345.6% of MemTotal" (won't fit the column)
759 */
760 SANITIZE(pmem);
709 761
710 smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy"); 762 smart_ulltoa5(s->memsize, memsize_str_buf, " mgtpezy");
711 /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */ 763 /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */
712 n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid)); 764 n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid));
713 ppu = ppubuf; 765 ppu = ppubuf;
@@ -736,27 +788,23 @@ typedef struct { unsigned quot, rem; } bb_div_t;
736 ppu[6+6+8] = '\0'; /* truncate USER */ 788 ppu[6+6+8] = '\0'; /* truncate USER */
737 } 789 }
738 shortened: 790 shortened:
739 col = snprintf(line_buf, scr_width, 791 col = sprintf(G.line_buf,
740 "\n" "%s %s %.5s" FMT 792 "%s %s %.5s" FMT
741 IF_FEATURE_TOP_SMP_PROCESS(" %3d") 793 IF_FEATURE_TOP_SMP_PROCESS(" %3d")
742 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) 794 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
743 " ", 795 " ",
744 ppu, 796 ppu,
745 s->state, vsz_str_buf, 797 s->state, memsize_str_buf,
746 SHOW_STAT(pmem) 798 SHOW_STAT(pmem)
747 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) 799 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
748 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu)) 800 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
749 ); 801 );
750 if ((int)(scr_width - col) > 1) 802 cmdline_to_line_buf_and_print(col, s->pid, s->comm);
751 read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm);
752 fputs_stdout(line_buf);
753 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu, 803 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
754 cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */ 804 cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */
755 s++; 805 s++;
756 } 806 }
757 /* printf(" %d", hist_iterations); */ 807 /* printf(" %d", hist_iterations); */
758 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
759 fflush_all();
760} 808}
761#undef UPSCALE 809#undef UPSCALE
762#undef SHOW_STAT 810#undef SHOW_STAT
@@ -828,36 +876,34 @@ static int topmem_sort(char *a, char *b)
828} 876}
829 877
830/* display header info (meminfo / loadavg) */ 878/* display header info (meminfo / loadavg) */
831static void display_topmem_header(int scr_width, int *lines_rem_p) 879static void display_topmem_header(void)
832{ 880{
833 unsigned long meminfo[MI_MAX]; 881 unsigned long meminfo[MI_MAX];
834 882
835 parse_meminfo(meminfo); 883 parse_meminfo(meminfo);
836 884
837 snprintf(line_buf, LINE_BUF_SIZE, 885 sprintf(G.line_buf,
838 "Mem total:%lu anon:%lu map:%lu free:%lu", 886 "Mem total:%lu anon:%lu map:%lu free:%lu",
839 meminfo[MI_MEMTOTAL], 887 meminfo[MI_MEMTOTAL],
840 meminfo[MI_ANONPAGES], 888 meminfo[MI_ANONPAGES],
841 meminfo[MI_MAPPED], 889 meminfo[MI_MAPPED],
842 meminfo[MI_MEMFREE]); 890 meminfo[MI_MEMFREE]);
843 printf(OPT_BATCH_MODE ? "%.*s\n" : ESC"[H" ESC"[J" "%.*s\n", scr_width, line_buf); 891 print_line_buf();
844 892
845 snprintf(line_buf, LINE_BUF_SIZE, 893 sprintf(G.line_buf,
846 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", 894 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu",
847 meminfo[MI_SLAB], 895 meminfo[MI_SLAB],
848 meminfo[MI_BUFFERS], 896 meminfo[MI_BUFFERS],
849 meminfo[MI_CACHED], 897 meminfo[MI_CACHED],
850 meminfo[MI_DIRTY], 898 meminfo[MI_DIRTY],
851 meminfo[MI_WRITEBACK]); 899 meminfo[MI_WRITEBACK]);
852 printf("%.*s\n", scr_width, line_buf); 900 print_line_buf();
853 901
854 snprintf(line_buf, LINE_BUF_SIZE, 902 sprintf(G.line_buf,
855 "Swap total:%lu free:%lu", // TODO: % used? 903 "Swap total:%lu free:%lu", // TODO: % used?
856 meminfo[MI_SWAPTOTAL], 904 meminfo[MI_SWAPTOTAL],
857 meminfo[MI_SWAPFREE]); 905 meminfo[MI_SWAPFREE]);
858 printf("%.*s\n", scr_width, line_buf); 906 print_line_buf();
859
860 (*lines_rem_p) -= 3;
861} 907}
862 908
863/* see http://en.wikipedia.org/wiki/Tera */ 909/* see http://en.wikipedia.org/wiki/Tera */
@@ -870,75 +916,57 @@ static void ulltoa4_and_space(unsigned long long ul, char buf[5])
870 smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' '; 916 smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' ';
871} 917}
872 918
873static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width) 919static NOINLINE void display_topmem_process_list(void)
874{ 920{
875#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK"
876#define MIN_WIDTH sizeof(HDR_STR)
877 const topmem_status_t *s = topmem + G_scroll_ofs; 921 const topmem_status_t *s = topmem + G_scroll_ofs;
878 char *cp, ch; 922 char *cp, ch;
879 923
880 display_topmem_header(scr_width, &lines_rem); 924 display_topmem_header();
881 925
882 strcpy(line_buf, HDR_STR " COMMAND"); 926 strcpy(G.line_buf, " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK COMMAND");
883 /* Mark the ^FIELD^ we sort by */ 927 /* Mark the ^FIELD^ we sort by */
884 cp = &line_buf[5 + sort_field * 6]; 928 cp = &G.line_buf[5 + sort_field * 6];
885 ch = "^_"[inverted]; 929 ch = "^_"[inverted];
886 cp[6] = ch; 930 cp[6] = ch;
887 do *cp++ = ch; while (*cp == ' '); 931 do *cp++ = ch; while (*cp == ' ');
932 print_line_bold();
888 933
889 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width, line_buf); 934 if (G.lines_remaining > ntop - G_scroll_ofs)
890 lines_rem--; 935 G.lines_remaining = ntop - G_scroll_ofs;
891 936 while (G.lines_remaining > 0) {
892 if (lines_rem > ntop - G_scroll_ofs)
893 lines_rem = ntop - G_scroll_ofs;
894 while (--lines_rem >= 0) {
895 /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */ 937 /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
896 int n = sprintf(line_buf, "%5u ", s->pid); 938 int n = sprintf(G.line_buf, "%5u ", s->pid);
897 if (n > 7) { 939 if (n > 7) {
898 /* PID is 7 chars long (up to 4194304) */ 940 /* PID is 7 chars long (up to 4194304) */
899 ulltoa4_and_space(s->vsz , &line_buf[8]); 941 ulltoa4_and_space(s->vsz , &G.line_buf[8]);
900 ulltoa4_and_space(s->vszrw, &line_buf[8+5]); 942 ulltoa4_and_space(s->vszrw, &G.line_buf[8+5]);
901 /* the next field (RSS) starts at 8+10 = 3*6 */ 943 /* the next field (RSS) starts at 8+10 = 3*6 */
902 } else { 944 } else {
903 if (n == 7) /* PID is 6 chars long */ 945 if (n == 7) /* PID is 6 chars long */
904 ulltoa4_and_space(s->vsz, &line_buf[7]); 946 ulltoa4_and_space(s->vsz, &G.line_buf[7]);
905 /* the next field (VSZRW) starts at 7+5 = 2*6 */ 947 /* the next field (VSZRW) starts at 7+5 = 2*6 */
906 else /* PID is 5 chars or less */ 948 else /* PID is 5 chars or less */
907 ulltoa5_and_space(s->vsz, &line_buf[6]); 949 ulltoa5_and_space(s->vsz, &G.line_buf[6]);
908 ulltoa5_and_space(s->vszrw, &line_buf[2*6]); 950 ulltoa5_and_space(s->vszrw, &G.line_buf[2*6]);
909 }
910 ulltoa5_and_space(s->rss , &line_buf[3*6]);
911 ulltoa5_and_space(s->rss_sh , &line_buf[4*6]);
912 ulltoa5_and_space(s->dirty , &line_buf[5*6]);
913 ulltoa5_and_space(s->dirty_sh, &line_buf[6*6]);
914 ulltoa5_and_space(s->stack , &line_buf[7*6]);
915 line_buf[8*6] = '\0';
916 if (scr_width > (int)MIN_WIDTH) {
917 read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
918 } 951 }
919 printf("\n""%.*s", scr_width, line_buf); 952 ulltoa5_and_space(s->rss , &G.line_buf[3*6]);
953 ulltoa5_and_space(s->rss_sh , &G.line_buf[4*6]);
954 ulltoa5_and_space(s->dirty , &G.line_buf[5*6]);
955 ulltoa5_and_space(s->dirty_sh, &G.line_buf[6*6]);
956 ulltoa5_and_space(s->stack , &G.line_buf[7*6]);
957 G.line_buf[8*6] = '\0';
958 cmdline_to_line_buf_and_print(8*6, s->pid, s->comm);
920 s++; 959 s++;
921 } 960 }
922 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
923 fflush_all();
924#undef HDR_STR
925#undef MIN_WIDTH
926} 961}
927 962
928#else 963#endif /* end TOPMEM support */
929void display_topmem_process_list(int lines_rem, int scr_width);
930int topmem_sort(char *a, char *b);
931#endif /* TOPMEM */
932
933/*
934 * end TOPMEM support
935 */
936 964
937enum { 965enum {
938 TOP_MASK = 0 966 TOP_MASK = 0
939 | PSSCAN_PID 967 | PSSCAN_PID
940 | PSSCAN_PPID 968 | PSSCAN_PPID
941 | PSSCAN_VSZ 969 | PSSCAN_RSS
942 | PSSCAN_STIME 970 | PSSCAN_STIME
943 | PSSCAN_UTIME 971 | PSSCAN_UTIME
944 | PSSCAN_STATE 972 | PSSCAN_STATE
@@ -950,7 +978,7 @@ enum {
950 | PSSCAN_SMAPS 978 | PSSCAN_SMAPS
951 | PSSCAN_COMM, 979 | PSSCAN_COMM,
952 EXIT_MASK = 0, 980 EXIT_MASK = 0,
953 NO_RESCAN_MASK = (unsigned)-1, 981 ONLY_REDRAW = (unsigned)-1,
954}; 982};
955 983
956#if ENABLE_FEATURE_TOP_INTERACTIVE 984#if ENABLE_FEATURE_TOP_INTERACTIVE
@@ -963,15 +991,22 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
963 } 991 }
964 992
965 while (1) { 993 while (1) {
966 int32_t c; 994 int32_t c, cc;
967 995
968 c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000); 996 c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
969 if (c == -1 && errno != EAGAIN) { 997 if (c == -1) {
970 /* error/EOF */ 998 if (errno != EAGAIN)
971 option_mask32 |= OPT_EOF; 999 /* error/EOF */
1000 option_mask32 |= OPT_EOF;
1001 /* else: timeout - rescan and refresh */
972 break; 1002 break;
973 } 1003 }
974 interval = 0; 1004 interval = 0;
1005 /* "continue" statements below return to do one additional
1006 * quick attempt to read a key. This prevents
1007 * long sequence of e.g. "nnnnnnnnnnnnnnnnnnnnnnnnnn"
1008 * to cause lots of rescans.
1009 */
975 1010
976 if (c == initial_settings.c_cc[VINTR]) 1011 if (c == initial_settings.c_cc[VINTR])
977 return EXIT_MASK; 1012 return EXIT_MASK;
@@ -1005,9 +1040,10 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1005 G_scroll_ofs = ntop - 1; 1040 G_scroll_ofs = ntop - 1;
1006 if (G_scroll_ofs < 0) 1041 if (G_scroll_ofs < 0)
1007 G_scroll_ofs = 0; 1042 G_scroll_ofs = 0;
1008 return NO_RESCAN_MASK; 1043 return ONLY_REDRAW;
1009 } 1044 }
1010 1045
1046 cc = c;
1011 c |= 0x20; /* lowercase */ 1047 c |= 0x20; /* lowercase */
1012 if (c == 'q') 1048 if (c == 'q')
1013 return EXIT_MASK; 1049 return EXIT_MASK;
@@ -1055,9 +1091,17 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1055 continue; 1091 continue;
1056 } 1092 }
1057# if ENABLE_FEATURE_TOPMEM 1093# if ENABLE_FEATURE_TOPMEM
1094 if (cc == 'S') {
1095 if (--sort_field < 0)
1096 sort_field = NUM_SORT_FIELD - 1;
1097 if (--sort_field < 0)
1098 sort_field = NUM_SORT_FIELD - 1;
1099 }
1058 if (c == 's') { 1100 if (c == 's') {
1059 scan_mask = TOPMEM_MASK;
1060 sort_field = (sort_field + 1) % NUM_SORT_FIELD; 1101 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1102 if (scan_mask == TOPMEM_MASK)
1103 return ONLY_REDRAW;
1104 scan_mask = TOPMEM_MASK;
1061 free(prev_hist); 1105 free(prev_hist);
1062 prev_hist = NULL; 1106 prev_hist = NULL;
1063 prev_hist_count = 0; 1107 prev_hist_count = 0;
@@ -1066,7 +1110,7 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1066# endif 1110# endif
1067 if (c == 'r') { 1111 if (c == 'r') {
1068 inverted ^= 1; 1112 inverted ^= 1;
1069 continue; 1113 return ONLY_REDRAW;
1070 } 1114 }
1071# if ENABLE_FEATURE_TOP_SMP_CPU 1115# if ENABLE_FEATURE_TOP_SMP_CPU
1072 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ 1116 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
@@ -1088,8 +1132,8 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1088 } 1132 }
1089# endif 1133# endif
1090# endif 1134# endif
1091 break; /* unknown key -> force refresh */ 1135 /* Unknown key. Eat remaining buffered input (if any) */
1092 } 1136 } /* while (1) */
1093 1137
1094 return scan_mask; 1138 return scan_mask;
1095} 1139}
@@ -1155,12 +1199,15 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1155{ 1199{
1156 duration_t interval; 1200 duration_t interval;
1157 int iterations; 1201 int iterations;
1158 unsigned col; 1202 unsigned opt;
1159 char *str_interval, *str_iterations; 1203 char *str_interval, *str_iterations;
1160 unsigned scan_mask = TOP_MASK; 1204 unsigned scan_mask = TOP_MASK;
1161 1205
1162 INIT_G(); 1206 INIT_G();
1163 1207
1208//worth it?
1209// setvbuf(stdout, /*buf*/ NULL, _IOFBF, /*size*/ 0);
1210
1164 interval = 5; /* default update interval is 5 seconds */ 1211 interval = 5; /* default update interval is 5 seconds */
1165 iterations = 0; /* infinite */ 1212 iterations = 0; /* infinite */
1166#if ENABLE_FEATURE_TOP_SMP_CPU 1213#if ENABLE_FEATURE_TOP_SMP_CPU
@@ -1172,13 +1219,13 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1172 1219
1173 /* all args are options; -n NUM */ 1220 /* all args are options; -n NUM */
1174 make_all_argv_opts(argv); /* options can be specified w/o dash */ 1221 make_all_argv_opts(argv); /* options can be specified w/o dash */
1175 col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations); 1222 opt = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations);
1176 /* NB: -m and -H are accepted even if not configured */ 1223 /* NB: -m and -H are accepted even if not configured */
1177#if ENABLE_FEATURE_TOPMEM 1224#if ENABLE_FEATURE_TOPMEM
1178 if (col & OPT_m) /* -m (busybox specific) */ 1225 if (opt & OPT_m) /* -m (busybox specific) */
1179 scan_mask = TOPMEM_MASK; 1226 scan_mask = TOPMEM_MASK;
1180#endif 1227#endif
1181 if (col & OPT_d) { 1228 if (opt & OPT_d) {
1182 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */ 1229 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */
1183 if (str_interval[0] == '-') 1230 if (str_interval[0] == '-')
1184 str_interval++; 1231 str_interval++;
@@ -1187,18 +1234,17 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1187 if (interval > INT_MAX / 1000) 1234 if (interval > INT_MAX / 1000)
1188 interval = INT_MAX / 1000; 1235 interval = INT_MAX / 1000;
1189 } 1236 }
1190 if (col & OPT_n) { 1237 if (opt & OPT_n) {
1191 if (str_iterations[0] == '-') 1238 if (str_iterations[0] == '-')
1192 str_iterations++; 1239 str_iterations++;
1193 iterations = xatou(str_iterations); 1240 iterations = xatou(str_iterations);
1194 } 1241 }
1195#if ENABLE_FEATURE_SHOW_THREADS 1242#if ENABLE_FEATURE_SHOW_THREADS
1196 if (col & OPT_H) { 1243 if (opt & OPT_H) {
1197 scan_mask |= PSSCAN_TASKS; 1244 scan_mask |= PSSCAN_TASKS;
1198 } 1245 }
1199#endif 1246#endif
1200 1247
1201 /* change to /proc */
1202 xchdir("/proc"); 1248 xchdir("/proc");
1203 1249
1204#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1250#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
@@ -1226,23 +1272,22 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1226#endif 1272#endif
1227 1273
1228 while (scan_mask != EXIT_MASK) { 1274 while (scan_mask != EXIT_MASK) {
1229 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask;) 1275 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask = scan_mask;)
1230 procps_status_t *p = NULL; 1276 procps_status_t *p = NULL;
1231 1277
1232 if (OPT_BATCH_MODE) { 1278 G.lines = INT_MAX;
1233 G.lines = INT_MAX; 1279 G.scr_width = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
1234 col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */ 1280 if (!OPT_BATCH_MODE) {
1235 } else {
1236 G.lines = 24; /* default */ 1281 G.lines = 24; /* default */
1237 col = 79; 1282 G.scr_width = 80;
1238 /* We output to stdout, we need size of stdout (not stdin)! */ 1283 /* We output to stdout, we need size of stdout (not stdin)! */
1239 get_terminal_width_height(STDOUT_FILENO, &col, &G.lines); 1284 get_terminal_width_height(STDOUT_FILENO, &G.scr_width, &G.lines);
1240 if (G.lines < 5 || col < 10) { 1285 if (G.lines < 5 || G.scr_width < 10) {
1241 sleep_for_duration(interval); 1286 sleep_for_duration(interval);
1242 continue; 1287 continue;
1243 } 1288 }
1244 if (col > LINE_BUF_SIZE - 2) 1289 if (G.scr_width > LINE_BUF_SIZE - 2)
1245 col = LINE_BUF_SIZE - 2; 1290 G.scr_width = LINE_BUF_SIZE - 2;
1246 } 1291 }
1247 1292
1248 /* read process IDs & status for all the processes */ 1293 /* read process IDs & status for all the processes */
@@ -1255,7 +1300,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1255 top = xrealloc_vector(top, 6, ntop++); 1300 top = xrealloc_vector(top, 6, ntop++);
1256 top[n].pid = p->pid; 1301 top[n].pid = p->pid;
1257 top[n].ppid = p->ppid; 1302 top[n].ppid = p->ppid;
1258 top[n].vsz = p->vsz; 1303 top[n].memsize = p->rss;
1259#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1304#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1260 top[n].ticks = p->stime + p->utime; 1305 top[n].ticks = p->stime + p->utime;
1261#endif 1306#endif
@@ -1268,20 +1313,20 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1268 } 1313 }
1269#if ENABLE_FEATURE_TOPMEM 1314#if ENABLE_FEATURE_TOPMEM
1270 else { /* TOPMEM */ 1315 else { /* TOPMEM */
1271 if (!(p->smaps.mapped_ro | p->smaps.mapped_rw)) 1316 if (!(p->mapped_ro | p->mapped_rw))
1272 continue; /* kernel threads are ignored */ 1317 continue; /* kernel threads are ignored */
1273 n = ntop; 1318 n = ntop;
1274 /* No bug here - top and topmem are the same */ 1319 /* No bug here - top and topmem are the same */
1275 top = xrealloc_vector(topmem, 6, ntop++); 1320 top = xrealloc_vector(topmem, 6, ntop++);
1276 strcpy(topmem[n].comm, p->comm); 1321 strcpy(topmem[n].comm, p->comm);
1277 topmem[n].pid = p->pid; 1322 topmem[n].pid = p->pid;
1278 topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro; 1323 topmem[n].vsz = p->mapped_rw + p->mapped_ro;
1279 topmem[n].vszrw = p->smaps.mapped_rw; 1324 topmem[n].vszrw = p->mapped_rw;
1280 topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty; 1325 topmem[n].rss_sh = p->shared_clean + p->shared_dirty;
1281 topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh; 1326 topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh;
1282 topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty; 1327 topmem[n].dirty = p->private_dirty + p->shared_dirty;
1283 topmem[n].dirty_sh = p->smaps.shared_dirty; 1328 topmem[n].dirty_sh = p->shared_dirty;
1284 topmem[n].stack = p->smaps.stack; 1329 topmem[n].stack = p->stack;
1285 } 1330 }
1286#endif 1331#endif
1287 } /* end of "while we read /proc" */ 1332 } /* end of "while we read /proc" */
@@ -1310,30 +1355,40 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1310 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); 1355 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1311 } 1356 }
1312#endif 1357#endif
1313 IF_FEATURE_TOP_INTERACTIVE(display:) 1358 IF_FEATURE_TOP_INTERACTIVE(redraw:)
1359 G.lines_remaining = G.lines;
1314 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) { 1360 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1315 display_process_list(G.lines, col); 1361 display_process_list();
1316 } 1362 }
1317#if ENABLE_FEATURE_TOPMEM 1363#if ENABLE_FEATURE_TOPMEM
1318 else { /* TOPMEM */ 1364 else { /* TOPMEM */
1319 display_topmem_process_list(G.lines, col); 1365 display_topmem_process_list();
1320 } 1366 }
1321#endif 1367#endif
1368 print_end();
1369 fflush_all();
1322 if (iterations >= 0 && !--iterations) 1370 if (iterations >= 0 && !--iterations)
1323 break; 1371 break;
1324#if !ENABLE_FEATURE_TOP_INTERACTIVE 1372#if !ENABLE_FEATURE_TOP_INTERACTIVE
1325 clearmems(); 1373 clearmems();
1326 sleep_for_duration(interval); 1374 sleep_for_duration(interval);
1327#else 1375#else
1328 new_mask = handle_input(scan_mask, interval); 1376 new_mask = handle_input(scan_mask,
1329 if (new_mask == NO_RESCAN_MASK) 1377 /* After "redraw with no rescan", have one
1330 goto display; 1378 * key timeout shorter that normal
1379 * (IOW: rescan sooner):
1380 */
1381 (new_mask == ONLY_REDRAW ? 1 : interval)
1382 );
1383 if (new_mask == ONLY_REDRAW)
1384 goto redraw;
1331 scan_mask = new_mask; 1385 scan_mask = new_mask;
1332 clearmems(); 1386 clearmems();
1333#endif 1387#endif
1334 } /* end of "while (not Q)" */ 1388 } /* end of "while (not Q)" */
1335 1389
1336 bb_putchar('\n'); 1390 if (!OPT_BATCH_MODE)
1391 bb_putchar('\n');
1337#if ENABLE_FEATURE_TOP_INTERACTIVE 1392#if ENABLE_FEATURE_TOP_INTERACTIVE
1338 reset_term(); 1393 reset_term();
1339#endif 1394#endif