diff options
Diffstat (limited to '')
-rw-r--r-- | procps/pmap.c | 141 | ||||
-rw-r--r-- | procps/top.c | 355 |
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 | ||
45 | static void print_smaprec(struct smaprec *currec, void *data) | 49 | struct 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 | |||
69 | static 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 | */ | ||
90 | static 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) \ | ||
121 | if (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 | ||
63 | static int procps_get_maps(pid_t pid, unsigned opt) | 176 | static 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 | ||
122 | typedef struct top_status_t { | 127 | typedef 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) | |||
241 | static int mem_sort(top_status_t *P, top_status_t *Q) | 248 | static 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 | ||
417 | static 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 | |||
431 | static 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 | |||
443 | static 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) */ |
412 | static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) | 452 | static 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 |
436 | static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) | 476 | static 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 | ||
516 | enum { | 555 | enum { |
@@ -564,52 +603,55 @@ static void parse_meminfo(unsigned long meminfo[MI_MAX]) | |||
564 | fclose(f); | 603 | fclose(f); |
565 | } | 604 | } |
566 | 605 | ||
567 | static unsigned long display_header(int scr_width, int *lines_rem_p) | 606 | static 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 | |||
615 | static 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 | ||
605 | static NOINLINE void display_process_list(int lines_rem, int scr_width) | 647 | static 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 |
624 | typedef 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) */ |
831 | static void display_topmem_header(int scr_width, int *lines_rem_p) | 879 | static 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 | ||
873 | static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width) | 919 | static 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 */ |
929 | void display_topmem_process_list(int lines_rem, int scr_width); | ||
930 | int topmem_sort(char *a, char *b); | ||
931 | #endif /* TOPMEM */ | ||
932 | |||
933 | /* | ||
934 | * end TOPMEM support | ||
935 | */ | ||
936 | 964 | ||
937 | enum { | 965 | enum { |
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 |