diff options
Diffstat (limited to '')
-rw-r--r-- | procps/pmap.c | 126 | ||||
-rw-r--r-- | procps/top.c | 351 |
2 files changed, 314 insertions, 163 deletions
diff --git a/procps/pmap.c b/procps/pmap.c index 49f7688d9..3069856a4 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,145 @@ 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); |
59 | 81 | ||
60 | printf(" %.4s %s\n", currec->smap_mode, currec->smap_name); | 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 | if (buf[0] == 'S' || buf[0] == 'P') { | ||
113 | #define SCAN(S, X) \ | ||
114 | if (memcmp(buf, S, sizeof(S)-1) == 0) { \ | ||
115 | tp = skip_whitespace(buf + sizeof(S)-1); \ | ||
116 | total->X += currec.X = fast_strtoul_10(&tp); \ | ||
117 | continue; \ | ||
118 | } | ||
119 | SCAN("Pss:" , smap_pss ); | ||
120 | SCAN("Swap:" , smap_swap ); | ||
121 | SCAN("Private_Dirty:", private_dirty); | ||
122 | #undef SCAN | ||
123 | } | ||
124 | tp = strchr(buf, '-'); | ||
125 | if (tp) { | ||
126 | // We reached next mapping - the line of this form: | ||
127 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME | ||
128 | |||
129 | // If we have a previous record, there's nothing more | ||
130 | // for it, print and clear currec | ||
131 | if (currec.smap_size) | ||
132 | print_smaprec(&currec); | ||
133 | free(currec.smap_name); | ||
134 | memset(&currec, 0, sizeof(currec)); | ||
135 | |||
136 | *tp = ' '; | ||
137 | tp = buf; | ||
138 | currec.smap_start = fast_strtoull_16(&tp); | ||
139 | currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10; | ||
140 | strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); | ||
141 | |||
142 | // skipping "rw-s FILEOFS M:m INODE " | ||
143 | tp = skip_fields(tp, 4); | ||
144 | tp = skip_whitespace(tp); // there may be many spaces, can't just "tp++" | ||
145 | p = strchrnul(tp, '\n'); | ||
146 | if (p != tp) { | ||
147 | currec.smap_name = xstrndup(tp, p - tp); | ||
148 | } | ||
149 | total->smap_size += currec.smap_size; | ||
150 | } | ||
151 | } // while (got line) | ||
152 | fclose(file); | ||
153 | |||
154 | if (currec.smap_size) | ||
155 | print_smaprec(&currec); | ||
156 | free(currec.smap_name); | ||
157 | |||
158 | return 0; | ||
61 | } | 159 | } |
62 | 160 | ||
63 | static int procps_get_maps(pid_t pid, unsigned opt) | 161 | static int procps_get_maps(pid_t pid, unsigned opt) |
64 | { | 162 | { |
65 | struct smaprec total; | 163 | struct smaprec total; |
66 | int ret; | 164 | int ret; |
67 | char buf[256]; | 165 | char buf[PMAP_BUFSZ]; |
166 | |||
167 | ret = read_cmdline(buf, sizeof(buf), pid, NULL); | ||
168 | if (ret < 0) | ||
169 | return ret; | ||
68 | 170 | ||
69 | read_cmdline(buf, sizeof(buf), pid, NULL); | ||
70 | printf("%u: %s\n", (int)pid, buf); | 171 | printf("%u: %s\n", (int)pid, buf); |
71 | 172 | ||
72 | if (!(opt & OPT_q) && (opt & OPT_x)) | 173 | if (!(opt & OPT_q) && (opt & OPT_x)) |
73 | puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping"); | 174 | puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping"); |
74 | 175 | ||
75 | memset(&total, 0, sizeof(total)); | 176 | memset(&total, 0, sizeof(total)); |
76 | 177 | ret = read_smaps(pid, buf, &total); | |
77 | ret = procps_read_smaps(pid, &total, print_smaprec, (void*)(uintptr_t)opt); | ||
78 | if (ret) | 178 | if (ret) |
79 | return ret; | 179 | return ret; |
80 | 180 | ||
81 | if (!(opt & OPT_q)) { | 181 | if (!(opt & OPT_q)) { |
82 | if (opt & OPT_x) | 182 | if (opt & OPT_x) |
83 | printf("--------" DASHES " ------ ------ ------ ------\n" | 183 | printf("--------" DASHES " ------ ------ ------ ------\n" |
84 | "total" TABS " %7lu %7lu %7lu %7lu\n", | 184 | "total kB %"SIZEWIDTHx"llu %7lu %7lu %7lu\n", |
85 | total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); | 185 | total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); |
86 | else | 186 | else |
87 | printf("mapped: %luK\n", total.smap_size); | 187 | printf(" total %"SIZEWIDTH"lluK\n", total.smap_size); |
88 | } | 188 | } |
89 | 189 | ||
90 | return 0; | 190 | return 0; |
diff --git a/procps/top.c b/procps/top.c index 09d31c673..96b3e2d4e 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 not_first_line; | ||
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,38 @@ 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.not_first_line) { | ||
424 | G.not_first_line = 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.not_first_line) { | ||
436 | // printf(OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL, G.scr_width - 1, G.line_buf); | ||
437 | // G.not_first_line = 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 | G.not_first_line = 0; /* next print will be "first line" (will clear the screen) */ | ||
447 | } | ||
448 | |||
410 | #if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS | 449 | #if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS |
411 | /* formats 7 char string (8 with terminating NUL) */ | 450 | /* formats 7 char string (8 with terminating NUL) */ |
412 | static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) | 451 | static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) |
@@ -433,7 +472,7 @@ static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) | |||
433 | #endif | 472 | #endif |
434 | 473 | ||
435 | #if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS | 474 | #if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS |
436 | static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) | 475 | static void display_cpus(void) |
437 | { | 476 | { |
438 | /* | 477 | /* |
439 | * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100% | 478 | * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100% |
@@ -469,8 +508,8 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) | |||
469 | # else | 508 | # else |
470 | /* Loop thru CPU(s) */ | 509 | /* Loop thru CPU(s) */ |
471 | n_cpu_lines = smp_cpu_info ? num_cpus : 1; | 510 | n_cpu_lines = smp_cpu_info ? num_cpus : 1; |
472 | if (n_cpu_lines > *lines_rem_p) | 511 | if (n_cpu_lines > G.lines_remaining) |
473 | n_cpu_lines = *lines_rem_p; | 512 | n_cpu_lines = G.lines_remaining; |
474 | 513 | ||
475 | for (i = 0; i < n_cpu_lines; i++) { | 514 | for (i = 0; i < n_cpu_lines; i++) { |
476 | p_jif = &cpu_jif[i]; | 515 | p_jif = &cpu_jif[i]; |
@@ -488,7 +527,7 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) | |||
488 | CALC_STAT(softirq); | 527 | CALC_STAT(softirq); |
489 | /*CALC_STAT(steal);*/ | 528 | /*CALC_STAT(steal);*/ |
490 | 529 | ||
491 | snprintf(scrbuf, scr_width, | 530 | sprintf(G.line_buf, |
492 | /* Barely fits in 79 chars when in "decimals" mode. */ | 531 | /* Barely fits in 79 chars when in "decimals" mode. */ |
493 | # if ENABLE_FEATURE_TOP_SMP_CPU | 532 | # if ENABLE_FEATURE_TOP_SMP_CPU |
494 | "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", | 533 | "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", |
@@ -501,16 +540,15 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) | |||
501 | /*, SHOW_STAT(steal) - what is this 'steal' thing? */ | 540 | /*, SHOW_STAT(steal) - what is this 'steal' thing? */ |
502 | /* I doubt anyone wants to know it */ | 541 | /* I doubt anyone wants to know it */ |
503 | ); | 542 | ); |
504 | puts(scrbuf); | 543 | print_line_buf(); |
505 | } | 544 | } |
506 | } | 545 | } |
507 | # undef SHOW_STAT | 546 | # undef SHOW_STAT |
508 | # undef CALC_STAT | 547 | # undef CALC_STAT |
509 | # undef FMT | 548 | # undef FMT |
510 | *lines_rem_p -= i; | ||
511 | } | 549 | } |
512 | #else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */ | 550 | #else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */ |
513 | # define display_cpus(scr_width, scrbuf, lines_rem) ((void)0) | 551 | # define display_cpus() ((void)0) |
514 | #endif | 552 | #endif |
515 | 553 | ||
516 | enum { | 554 | enum { |
@@ -564,52 +602,55 @@ static void parse_meminfo(unsigned long meminfo[MI_MAX]) | |||
564 | fclose(f); | 602 | fclose(f); |
565 | } | 603 | } |
566 | 604 | ||
567 | static unsigned long display_header(int scr_width, int *lines_rem_p) | 605 | static void cmdline_to_line_buf_and_print(unsigned offset, unsigned pid, const char *comm) |
606 | { | ||
607 | int width = G.scr_width - offset; | ||
608 | if (width > 1) /* wider than to fit just the NUL? */ | ||
609 | read_cmdline(G.line_buf + offset, width, pid, comm); | ||
610 | //TODO: read_cmdline() sanitizes control chars, but not chars above 0x7e | ||
611 | print_line_buf(); | ||
612 | } | ||
613 | |||
614 | static unsigned long display_header(void) | ||
568 | { | 615 | { |
569 | char scrbuf[100]; /* [80] was a bit too low on 8Gb ram box */ | ||
570 | char *buf; | 616 | char *buf; |
571 | unsigned long meminfo[MI_MAX]; | 617 | unsigned long meminfo[MI_MAX]; |
572 | 618 | ||
573 | parse_meminfo(meminfo); | 619 | parse_meminfo(meminfo); |
574 | 620 | ||
575 | /* Output memory info */ | 621 | /* Output memory info */ |
576 | if (scr_width > (int)sizeof(scrbuf)) | 622 | 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", | 623 | "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", |
580 | meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE], | 624 | meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE], |
581 | meminfo[MI_MEMFREE], | 625 | meminfo[MI_MEMFREE], |
582 | meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM], | 626 | meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM], |
583 | meminfo[MI_BUFFERS], | 627 | meminfo[MI_BUFFERS], |
584 | meminfo[MI_CACHED]); | 628 | meminfo[MI_CACHED]); |
585 | /* Go to top & clear to the end of screen */ | 629 | print_line_buf(); |
586 | printf(OPT_BATCH_MODE ? "%s\n" : ESC"[H" ESC"[J" "%s\n", scrbuf); | ||
587 | (*lines_rem_p)--; | ||
588 | 630 | ||
589 | /* Display CPU time split as percentage of total time. | 631 | /* Display CPU time split as percentage of total time. |
590 | * This displays either a cumulative line or one line per CPU. | 632 | * This displays either a cumulative line or one line per CPU. |
591 | */ | 633 | */ |
592 | display_cpus(scr_width, scrbuf, lines_rem_p); | 634 | display_cpus(); |
593 | 635 | ||
594 | /* Read load average as a string */ | 636 | /* Read load average as a string */ |
595 | buf = stpcpy(scrbuf, "Load average: "); | 637 | buf = stpcpy(G.line_buf, "Load average: "); |
596 | open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: ")); | 638 | open_read_close("loadavg", buf, sizeof(G.line_buf) - sizeof("Load average: ")); |
597 | scrbuf[scr_width - 1] = '\0'; | 639 | G.line_buf[sizeof(G.line_buf) - 1] = '\0'; /* paranoia */ |
598 | strchrnul(buf, '\n')[0] = '\0'; | 640 | strchrnul(buf, '\n')[0] = '\0'; |
599 | puts(scrbuf); | 641 | print_line_buf(); |
600 | (*lines_rem_p)--; | ||
601 | 642 | ||
602 | return meminfo[MI_MEMTOTAL]; | 643 | return meminfo[MI_MEMTOTAL]; |
603 | } | 644 | } |
604 | 645 | ||
605 | static NOINLINE void display_process_list(int lines_rem, int scr_width) | 646 | static NOINLINE void display_process_list(void) |
606 | { | 647 | { |
607 | enum { | 648 | enum { |
608 | BITS_PER_INT = sizeof(int) * 8 | 649 | BITS_PER_INT = sizeof(int) * 8 |
609 | }; | 650 | }; |
610 | 651 | ||
611 | top_status_t *s; | 652 | top_status_t *s; |
612 | unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */ | 653 | unsigned long total_memory = display_header(); |
613 | /* xxx_shift and xxx_scale variables allow us to replace | 654 | /* xxx_shift and xxx_scale variables allow us to replace |
614 | * expensive divides with multiply and shift */ | 655 | * expensive divides with multiply and shift */ |
615 | unsigned pmem_shift, pmem_scale, pmem_half; | 656 | unsigned pmem_shift, pmem_scale, pmem_half; |
@@ -621,7 +662,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width) | |||
621 | 662 | ||
622 | #if ENABLE_FEATURE_TOP_DECIMALS | 663 | #if ENABLE_FEATURE_TOP_DECIMALS |
623 | # define UPSCALE 1000 | 664 | # define UPSCALE 1000 |
624 | typedef struct { unsigned quot, rem; } bb_div_t; | 665 | typedef struct { unsigned quot, rem; } bb_div_t; |
625 | /* Used to have "div_t name = div((val), 10)" here | 666 | /* Used to have "div_t name = div((val), 10)" here |
626 | * (IOW: intended to use libc-compatible way to divide and use | 667 | * (IOW: intended to use libc-compatible way to divide and use |
627 | * both result and remainder, but musl does not inline div()...) | 668 | * both result and remainder, but musl does not inline div()...) |
@@ -629,28 +670,34 @@ typedef struct { unsigned quot, rem; } bb_div_t; | |||
629 | */ | 670 | */ |
630 | # define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 } | 671 | # define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 } |
631 | # define SHOW_STAT(name) name.quot, '0'+name.rem | 672 | # define SHOW_STAT(name) name.quot, '0'+name.rem |
673 | # define SANITIZE(name) if (name.quot > 99) name.quot = 99, name.rem = (unsigned char)('+' - '0') | ||
632 | # define FMT "%3u.%c" | 674 | # define FMT "%3u.%c" |
633 | #else | 675 | #else |
634 | # define UPSCALE 100 | 676 | # define UPSCALE 100 |
635 | # define CALC_STAT(name, val) unsigned name = (val) | 677 | # define CALC_STAT(name, val) unsigned name = (val) |
678 | # define SANITIZE(name) if (name > 99) name = 99 | ||
636 | # define SHOW_STAT(name) name | 679 | # define SHOW_STAT(name) name |
637 | # define FMT "%4u%%" | 680 | # define FMT "%4u%%" |
638 | #endif | 681 | #endif |
639 | 682 | ||
640 | /* what info of the processes is shown */ | 683 | 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") | 684 | IF_FEATURE_TOP_SMP_PROCESS(" CPU") |
644 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU") | 685 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU") |
645 | " COMMAND"); | 686 | " COMMAND"); |
646 | lines_rem--; | 687 | print_line_bold(); |
647 | 688 | ||
648 | /* | 689 | /* %RSS = s->memsize / MemTotal * 100% |
649 | * %VSZ = s->vsz/MemTotal | 690 | * Calculate this with multiply and shift. Example: |
691 | * shift = 12 | ||
692 | * scale = 100 * 0x1000 / total_memory | ||
693 | * percent_mem = (size_mem * scale) >> shift | ||
694 | * ~= (size_mem >> shift) * scale | ||
695 | * ~= (size_mem >> shift) * 100 * (1 << shift) / total_memory | ||
696 | * ~= size_mem * 100 / total_memory | ||
650 | */ | 697 | */ |
651 | pmem_shift = BITS_PER_INT-11; | 698 | pmem_shift = BITS_PER_INT-11; |
652 | pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory; | 699 | 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 */ | 700 | /* s->memsize is in kb. we want (s->memsize * pmem_scale) to never overflow */ |
654 | while (pmem_scale >= 512) { | 701 | while (pmem_scale >= 512) { |
655 | pmem_scale /= 4; | 702 | pmem_scale /= 4; |
656 | pmem_shift -= 2; | 703 | pmem_shift -= 2; |
@@ -689,25 +736,29 @@ typedef struct { unsigned quot, rem; } bb_div_t; | |||
689 | pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2); | 736 | pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2); |
690 | /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ | 737 | /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ |
691 | #endif | 738 | #endif |
739 | if (G.lines_remaining > ntop - G_scroll_ofs) | ||
740 | G.lines_remaining = ntop - G_scroll_ofs; | ||
692 | 741 | ||
693 | /* Ok, all preliminary data is ready, go through the list */ | 742 | /* 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; | 743 | s = top + G_scroll_ofs; |
698 | while (--lines_rem >= 0) { | 744 | while (G.lines_remaining > 0) { |
699 | int n; | 745 | int n; |
700 | char *ppu; | 746 | char *ppu; |
701 | char ppubuf[sizeof(int)*3 * 2 + 12]; | 747 | char ppubuf[sizeof(int)*3 * 2 + 12]; |
702 | char vsz_str_buf[8]; | 748 | char memsize_str_buf[8]; |
703 | unsigned col; | 749 | unsigned col; |
704 | 750 | ||
705 | CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift); | 751 | CALC_STAT(pmem, (s->memsize*pmem_scale + pmem_half) >> pmem_shift); |
706 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 752 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
707 | CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift); | 753 | CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift); |
708 | #endif | 754 | #endif |
755 | /* VSZ can be much larger than total memory | ||
756 | * (seen values close to 2Tbyte), don't try to display | ||
757 | * "uses 12345.6% of MemTotal" (won't fit the column) | ||
758 | */ | ||
759 | SANITIZE(pmem); | ||
709 | 760 | ||
710 | smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy"); | 761 | smart_ulltoa5(s->memsize, memsize_str_buf, " mgtpezy"); |
711 | /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */ | 762 | /* 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)); | 763 | n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid)); |
713 | ppu = ppubuf; | 764 | ppu = ppubuf; |
@@ -736,27 +787,23 @@ typedef struct { unsigned quot, rem; } bb_div_t; | |||
736 | ppu[6+6+8] = '\0'; /* truncate USER */ | 787 | ppu[6+6+8] = '\0'; /* truncate USER */ |
737 | } | 788 | } |
738 | shortened: | 789 | shortened: |
739 | col = snprintf(line_buf, scr_width, | 790 | col = sprintf(G.line_buf, |
740 | "\n" "%s %s %.5s" FMT | 791 | "%s %s %.5s" FMT |
741 | IF_FEATURE_TOP_SMP_PROCESS(" %3d") | 792 | IF_FEATURE_TOP_SMP_PROCESS(" %3d") |
742 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) | 793 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) |
743 | " ", | 794 | " ", |
744 | ppu, | 795 | ppu, |
745 | s->state, vsz_str_buf, | 796 | s->state, memsize_str_buf, |
746 | SHOW_STAT(pmem) | 797 | SHOW_STAT(pmem) |
747 | IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) | 798 | IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) |
748 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu)) | 799 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu)) |
749 | ); | 800 | ); |
750 | if ((int)(scr_width - col) > 1) | 801 | 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, | 802 | /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu, |
754 | cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */ | 803 | cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */ |
755 | s++; | 804 | s++; |
756 | } | 805 | } |
757 | /* printf(" %d", hist_iterations); */ | 806 | /* printf(" %d", hist_iterations); */ |
758 | bb_putchar(OPT_BATCH_MODE ? '\n' : '\r'); | ||
759 | fflush_all(); | ||
760 | } | 807 | } |
761 | #undef UPSCALE | 808 | #undef UPSCALE |
762 | #undef SHOW_STAT | 809 | #undef SHOW_STAT |
@@ -828,36 +875,34 @@ static int topmem_sort(char *a, char *b) | |||
828 | } | 875 | } |
829 | 876 | ||
830 | /* display header info (meminfo / loadavg) */ | 877 | /* display header info (meminfo / loadavg) */ |
831 | static void display_topmem_header(int scr_width, int *lines_rem_p) | 878 | static void display_topmem_header(void) |
832 | { | 879 | { |
833 | unsigned long meminfo[MI_MAX]; | 880 | unsigned long meminfo[MI_MAX]; |
834 | 881 | ||
835 | parse_meminfo(meminfo); | 882 | parse_meminfo(meminfo); |
836 | 883 | ||
837 | snprintf(line_buf, LINE_BUF_SIZE, | 884 | sprintf(G.line_buf, |
838 | "Mem total:%lu anon:%lu map:%lu free:%lu", | 885 | "Mem total:%lu anon:%lu map:%lu free:%lu", |
839 | meminfo[MI_MEMTOTAL], | 886 | meminfo[MI_MEMTOTAL], |
840 | meminfo[MI_ANONPAGES], | 887 | meminfo[MI_ANONPAGES], |
841 | meminfo[MI_MAPPED], | 888 | meminfo[MI_MAPPED], |
842 | meminfo[MI_MEMFREE]); | 889 | meminfo[MI_MEMFREE]); |
843 | printf(OPT_BATCH_MODE ? "%.*s\n" : ESC"[H" ESC"[J" "%.*s\n", scr_width, line_buf); | 890 | print_line_buf(); |
844 | 891 | ||
845 | snprintf(line_buf, LINE_BUF_SIZE, | 892 | sprintf(G.line_buf, |
846 | " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", | 893 | " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", |
847 | meminfo[MI_SLAB], | 894 | meminfo[MI_SLAB], |
848 | meminfo[MI_BUFFERS], | 895 | meminfo[MI_BUFFERS], |
849 | meminfo[MI_CACHED], | 896 | meminfo[MI_CACHED], |
850 | meminfo[MI_DIRTY], | 897 | meminfo[MI_DIRTY], |
851 | meminfo[MI_WRITEBACK]); | 898 | meminfo[MI_WRITEBACK]); |
852 | printf("%.*s\n", scr_width, line_buf); | 899 | print_line_buf(); |
853 | 900 | ||
854 | snprintf(line_buf, LINE_BUF_SIZE, | 901 | sprintf(G.line_buf, |
855 | "Swap total:%lu free:%lu", // TODO: % used? | 902 | "Swap total:%lu free:%lu", // TODO: % used? |
856 | meminfo[MI_SWAPTOTAL], | 903 | meminfo[MI_SWAPTOTAL], |
857 | meminfo[MI_SWAPFREE]); | 904 | meminfo[MI_SWAPFREE]); |
858 | printf("%.*s\n", scr_width, line_buf); | 905 | print_line_buf(); |
859 | |||
860 | (*lines_rem_p) -= 3; | ||
861 | } | 906 | } |
862 | 907 | ||
863 | /* see http://en.wikipedia.org/wiki/Tera */ | 908 | /* see http://en.wikipedia.org/wiki/Tera */ |
@@ -870,75 +915,57 @@ static void ulltoa4_and_space(unsigned long long ul, char buf[5]) | |||
870 | smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' '; | 915 | smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' '; |
871 | } | 916 | } |
872 | 917 | ||
873 | static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width) | 918 | static NOINLINE void display_topmem_process_list(void) |
874 | { | 919 | { |
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; | 920 | const topmem_status_t *s = topmem + G_scroll_ofs; |
878 | char *cp, ch; | 921 | char *cp, ch; |
879 | 922 | ||
880 | display_topmem_header(scr_width, &lines_rem); | 923 | display_topmem_header(); |
881 | 924 | ||
882 | strcpy(line_buf, HDR_STR " COMMAND"); | 925 | strcpy(G.line_buf, " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK COMMAND"); |
883 | /* Mark the ^FIELD^ we sort by */ | 926 | /* Mark the ^FIELD^ we sort by */ |
884 | cp = &line_buf[5 + sort_field * 6]; | 927 | cp = &G.line_buf[5 + sort_field * 6]; |
885 | ch = "^_"[inverted]; | 928 | ch = "^_"[inverted]; |
886 | cp[6] = ch; | 929 | cp[6] = ch; |
887 | do *cp++ = ch; while (*cp == ' '); | 930 | do *cp++ = ch; while (*cp == ' '); |
931 | print_line_bold(); | ||
888 | 932 | ||
889 | printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width, line_buf); | 933 | if (G.lines_remaining > ntop - G_scroll_ofs) |
890 | lines_rem--; | 934 | G.lines_remaining = ntop - G_scroll_ofs; |
891 | 935 | 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 */ | 936 | /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */ |
896 | int n = sprintf(line_buf, "%5u ", s->pid); | 937 | int n = sprintf(G.line_buf, "%5u ", s->pid); |
897 | if (n > 7) { | 938 | if (n > 7) { |
898 | /* PID is 7 chars long (up to 4194304) */ | 939 | /* PID is 7 chars long (up to 4194304) */ |
899 | ulltoa4_and_space(s->vsz , &line_buf[8]); | 940 | ulltoa4_and_space(s->vsz , &G.line_buf[8]); |
900 | ulltoa4_and_space(s->vszrw, &line_buf[8+5]); | 941 | ulltoa4_and_space(s->vszrw, &G.line_buf[8+5]); |
901 | /* the next field (RSS) starts at 8+10 = 3*6 */ | 942 | /* the next field (RSS) starts at 8+10 = 3*6 */ |
902 | } else { | 943 | } else { |
903 | if (n == 7) /* PID is 6 chars long */ | 944 | if (n == 7) /* PID is 6 chars long */ |
904 | ulltoa4_and_space(s->vsz, &line_buf[7]); | 945 | ulltoa4_and_space(s->vsz, &G.line_buf[7]); |
905 | /* the next field (VSZRW) starts at 7+5 = 2*6 */ | 946 | /* the next field (VSZRW) starts at 7+5 = 2*6 */ |
906 | else /* PID is 5 chars or less */ | 947 | else /* PID is 5 chars or less */ |
907 | ulltoa5_and_space(s->vsz, &line_buf[6]); | 948 | ulltoa5_and_space(s->vsz, &G.line_buf[6]); |
908 | ulltoa5_and_space(s->vszrw, &line_buf[2*6]); | 949 | 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 | } | 950 | } |
919 | printf("\n""%.*s", scr_width, line_buf); | 951 | ulltoa5_and_space(s->rss , &G.line_buf[3*6]); |
952 | ulltoa5_and_space(s->rss_sh , &G.line_buf[4*6]); | ||
953 | ulltoa5_and_space(s->dirty , &G.line_buf[5*6]); | ||
954 | ulltoa5_and_space(s->dirty_sh, &G.line_buf[6*6]); | ||
955 | ulltoa5_and_space(s->stack , &G.line_buf[7*6]); | ||
956 | G.line_buf[8*6] = '\0'; | ||
957 | cmdline_to_line_buf_and_print(8*6, s->pid, s->comm); | ||
920 | s++; | 958 | s++; |
921 | } | 959 | } |
922 | bb_putchar(OPT_BATCH_MODE ? '\n' : '\r'); | ||
923 | fflush_all(); | ||
924 | #undef HDR_STR | ||
925 | #undef MIN_WIDTH | ||
926 | } | 960 | } |
927 | 961 | ||
928 | #else | 962 | #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 | 963 | ||
937 | enum { | 964 | enum { |
938 | TOP_MASK = 0 | 965 | TOP_MASK = 0 |
939 | | PSSCAN_PID | 966 | | PSSCAN_PID |
940 | | PSSCAN_PPID | 967 | | PSSCAN_PPID |
941 | | PSSCAN_VSZ | 968 | | PSSCAN_RSS |
942 | | PSSCAN_STIME | 969 | | PSSCAN_STIME |
943 | | PSSCAN_UTIME | 970 | | PSSCAN_UTIME |
944 | | PSSCAN_STATE | 971 | | PSSCAN_STATE |
@@ -950,7 +977,7 @@ enum { | |||
950 | | PSSCAN_SMAPS | 977 | | PSSCAN_SMAPS |
951 | | PSSCAN_COMM, | 978 | | PSSCAN_COMM, |
952 | EXIT_MASK = 0, | 979 | EXIT_MASK = 0, |
953 | NO_RESCAN_MASK = (unsigned)-1, | 980 | ONLY_REDRAW = (unsigned)-1, |
954 | }; | 981 | }; |
955 | 982 | ||
956 | #if ENABLE_FEATURE_TOP_INTERACTIVE | 983 | #if ENABLE_FEATURE_TOP_INTERACTIVE |
@@ -963,15 +990,22 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval) | |||
963 | } | 990 | } |
964 | 991 | ||
965 | while (1) { | 992 | while (1) { |
966 | int32_t c; | 993 | int32_t c, cc; |
967 | 994 | ||
968 | c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000); | 995 | c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000); |
969 | if (c == -1 && errno != EAGAIN) { | 996 | if (c == -1) { |
970 | /* error/EOF */ | 997 | if (errno != EAGAIN) |
971 | option_mask32 |= OPT_EOF; | 998 | /* error/EOF */ |
999 | option_mask32 |= OPT_EOF; | ||
1000 | /* else: timeout - rescan and refresh */ | ||
972 | break; | 1001 | break; |
973 | } | 1002 | } |
974 | interval = 0; | 1003 | interval = 0; |
1004 | /* "continue" statements below return to do one additional | ||
1005 | * quick attempt to read a key. This prevents | ||
1006 | * long sequence of e.g. "nnnnnnnnnnnnnnnnnnnnnnnnnn" | ||
1007 | * to cause lots of rescans. | ||
1008 | */ | ||
975 | 1009 | ||
976 | if (c == initial_settings.c_cc[VINTR]) | 1010 | if (c == initial_settings.c_cc[VINTR]) |
977 | return EXIT_MASK; | 1011 | return EXIT_MASK; |
@@ -1005,9 +1039,10 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval) | |||
1005 | G_scroll_ofs = ntop - 1; | 1039 | G_scroll_ofs = ntop - 1; |
1006 | if (G_scroll_ofs < 0) | 1040 | if (G_scroll_ofs < 0) |
1007 | G_scroll_ofs = 0; | 1041 | G_scroll_ofs = 0; |
1008 | return NO_RESCAN_MASK; | 1042 | return ONLY_REDRAW; |
1009 | } | 1043 | } |
1010 | 1044 | ||
1045 | cc = c; | ||
1011 | c |= 0x20; /* lowercase */ | 1046 | c |= 0x20; /* lowercase */ |
1012 | if (c == 'q') | 1047 | if (c == 'q') |
1013 | return EXIT_MASK; | 1048 | return EXIT_MASK; |
@@ -1055,9 +1090,17 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval) | |||
1055 | continue; | 1090 | continue; |
1056 | } | 1091 | } |
1057 | # if ENABLE_FEATURE_TOPMEM | 1092 | # if ENABLE_FEATURE_TOPMEM |
1093 | if (cc == 'S') { | ||
1094 | if (--sort_field < 0) | ||
1095 | sort_field = NUM_SORT_FIELD - 1; | ||
1096 | if (--sort_field < 0) | ||
1097 | sort_field = NUM_SORT_FIELD - 1; | ||
1098 | } | ||
1058 | if (c == 's') { | 1099 | if (c == 's') { |
1059 | scan_mask = TOPMEM_MASK; | ||
1060 | sort_field = (sort_field + 1) % NUM_SORT_FIELD; | 1100 | sort_field = (sort_field + 1) % NUM_SORT_FIELD; |
1101 | if (scan_mask == TOPMEM_MASK) | ||
1102 | return ONLY_REDRAW; | ||
1103 | scan_mask = TOPMEM_MASK; | ||
1061 | free(prev_hist); | 1104 | free(prev_hist); |
1062 | prev_hist = NULL; | 1105 | prev_hist = NULL; |
1063 | prev_hist_count = 0; | 1106 | prev_hist_count = 0; |
@@ -1066,7 +1109,7 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval) | |||
1066 | # endif | 1109 | # endif |
1067 | if (c == 'r') { | 1110 | if (c == 'r') { |
1068 | inverted ^= 1; | 1111 | inverted ^= 1; |
1069 | continue; | 1112 | return ONLY_REDRAW; |
1070 | } | 1113 | } |
1071 | # if ENABLE_FEATURE_TOP_SMP_CPU | 1114 | # if ENABLE_FEATURE_TOP_SMP_CPU |
1072 | /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ | 1115 | /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ |
@@ -1088,8 +1131,8 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval) | |||
1088 | } | 1131 | } |
1089 | # endif | 1132 | # endif |
1090 | # endif | 1133 | # endif |
1091 | break; /* unknown key -> force refresh */ | 1134 | /* Unknown key. Eat remaining buffered input (if any) */ |
1092 | } | 1135 | } /* while (1) */ |
1093 | 1136 | ||
1094 | return scan_mask; | 1137 | return scan_mask; |
1095 | } | 1138 | } |
@@ -1155,7 +1198,7 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
1155 | { | 1198 | { |
1156 | duration_t interval; | 1199 | duration_t interval; |
1157 | int iterations; | 1200 | int iterations; |
1158 | unsigned col; | 1201 | unsigned opt; |
1159 | char *str_interval, *str_iterations; | 1202 | char *str_interval, *str_iterations; |
1160 | unsigned scan_mask = TOP_MASK; | 1203 | unsigned scan_mask = TOP_MASK; |
1161 | 1204 | ||
@@ -1172,13 +1215,13 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
1172 | 1215 | ||
1173 | /* all args are options; -n NUM */ | 1216 | /* all args are options; -n NUM */ |
1174 | make_all_argv_opts(argv); /* options can be specified w/o dash */ | 1217 | make_all_argv_opts(argv); /* options can be specified w/o dash */ |
1175 | col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations); | 1218 | opt = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations); |
1176 | /* NB: -m and -H are accepted even if not configured */ | 1219 | /* NB: -m and -H are accepted even if not configured */ |
1177 | #if ENABLE_FEATURE_TOPMEM | 1220 | #if ENABLE_FEATURE_TOPMEM |
1178 | if (col & OPT_m) /* -m (busybox specific) */ | 1221 | if (opt & OPT_m) /* -m (busybox specific) */ |
1179 | scan_mask = TOPMEM_MASK; | 1222 | scan_mask = TOPMEM_MASK; |
1180 | #endif | 1223 | #endif |
1181 | if (col & OPT_d) { | 1224 | if (opt & OPT_d) { |
1182 | /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */ | 1225 | /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */ |
1183 | if (str_interval[0] == '-') | 1226 | if (str_interval[0] == '-') |
1184 | str_interval++; | 1227 | str_interval++; |
@@ -1187,18 +1230,17 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
1187 | if (interval > INT_MAX / 1000) | 1230 | if (interval > INT_MAX / 1000) |
1188 | interval = INT_MAX / 1000; | 1231 | interval = INT_MAX / 1000; |
1189 | } | 1232 | } |
1190 | if (col & OPT_n) { | 1233 | if (opt & OPT_n) { |
1191 | if (str_iterations[0] == '-') | 1234 | if (str_iterations[0] == '-') |
1192 | str_iterations++; | 1235 | str_iterations++; |
1193 | iterations = xatou(str_iterations); | 1236 | iterations = xatou(str_iterations); |
1194 | } | 1237 | } |
1195 | #if ENABLE_FEATURE_SHOW_THREADS | 1238 | #if ENABLE_FEATURE_SHOW_THREADS |
1196 | if (col & OPT_H) { | 1239 | if (opt & OPT_H) { |
1197 | scan_mask |= PSSCAN_TASKS; | 1240 | scan_mask |= PSSCAN_TASKS; |
1198 | } | 1241 | } |
1199 | #endif | 1242 | #endif |
1200 | 1243 | ||
1201 | /* change to /proc */ | ||
1202 | xchdir("/proc"); | 1244 | xchdir("/proc"); |
1203 | 1245 | ||
1204 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 1246 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
@@ -1226,23 +1268,22 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
1226 | #endif | 1268 | #endif |
1227 | 1269 | ||
1228 | while (scan_mask != EXIT_MASK) { | 1270 | while (scan_mask != EXIT_MASK) { |
1229 | IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask;) | 1271 | IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask = scan_mask;) |
1230 | procps_status_t *p = NULL; | 1272 | procps_status_t *p = NULL; |
1231 | 1273 | ||
1232 | if (OPT_BATCH_MODE) { | 1274 | G.lines = INT_MAX; |
1233 | G.lines = INT_MAX; | 1275 | G.scr_width = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */ |
1234 | col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */ | 1276 | if (!OPT_BATCH_MODE) { |
1235 | } else { | ||
1236 | G.lines = 24; /* default */ | 1277 | G.lines = 24; /* default */ |
1237 | col = 79; | 1278 | G.scr_width = 80; |
1238 | /* We output to stdout, we need size of stdout (not stdin)! */ | 1279 | /* We output to stdout, we need size of stdout (not stdin)! */ |
1239 | get_terminal_width_height(STDOUT_FILENO, &col, &G.lines); | 1280 | get_terminal_width_height(STDOUT_FILENO, &G.scr_width, &G.lines); |
1240 | if (G.lines < 5 || col < 10) { | 1281 | if (G.lines < 5 || G.scr_width < 10) { |
1241 | sleep_for_duration(interval); | 1282 | sleep_for_duration(interval); |
1242 | continue; | 1283 | continue; |
1243 | } | 1284 | } |
1244 | if (col > LINE_BUF_SIZE - 2) | 1285 | if (G.scr_width > LINE_BUF_SIZE - 2) |
1245 | col = LINE_BUF_SIZE - 2; | 1286 | G.scr_width = LINE_BUF_SIZE - 2; |
1246 | } | 1287 | } |
1247 | 1288 | ||
1248 | /* read process IDs & status for all the processes */ | 1289 | /* read process IDs & status for all the processes */ |
@@ -1255,7 +1296,7 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
1255 | top = xrealloc_vector(top, 6, ntop++); | 1296 | top = xrealloc_vector(top, 6, ntop++); |
1256 | top[n].pid = p->pid; | 1297 | top[n].pid = p->pid; |
1257 | top[n].ppid = p->ppid; | 1298 | top[n].ppid = p->ppid; |
1258 | top[n].vsz = p->vsz; | 1299 | top[n].memsize = p->rss; |
1259 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 1300 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
1260 | top[n].ticks = p->stime + p->utime; | 1301 | top[n].ticks = p->stime + p->utime; |
1261 | #endif | 1302 | #endif |
@@ -1268,20 +1309,20 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
1268 | } | 1309 | } |
1269 | #if ENABLE_FEATURE_TOPMEM | 1310 | #if ENABLE_FEATURE_TOPMEM |
1270 | else { /* TOPMEM */ | 1311 | else { /* TOPMEM */ |
1271 | if (!(p->smaps.mapped_ro | p->smaps.mapped_rw)) | 1312 | if (!(p->mapped_ro | p->mapped_rw)) |
1272 | continue; /* kernel threads are ignored */ | 1313 | continue; /* kernel threads are ignored */ |
1273 | n = ntop; | 1314 | n = ntop; |
1274 | /* No bug here - top and topmem are the same */ | 1315 | /* No bug here - top and topmem are the same */ |
1275 | top = xrealloc_vector(topmem, 6, ntop++); | 1316 | top = xrealloc_vector(topmem, 6, ntop++); |
1276 | strcpy(topmem[n].comm, p->comm); | 1317 | strcpy(topmem[n].comm, p->comm); |
1277 | topmem[n].pid = p->pid; | 1318 | topmem[n].pid = p->pid; |
1278 | topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro; | 1319 | topmem[n].vsz = p->mapped_rw + p->mapped_ro; |
1279 | topmem[n].vszrw = p->smaps.mapped_rw; | 1320 | topmem[n].vszrw = p->mapped_rw; |
1280 | topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty; | 1321 | 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; | 1322 | 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; | 1323 | topmem[n].dirty = p->private_dirty + p->shared_dirty; |
1283 | topmem[n].dirty_sh = p->smaps.shared_dirty; | 1324 | topmem[n].dirty_sh = p->shared_dirty; |
1284 | topmem[n].stack = p->smaps.stack; | 1325 | topmem[n].stack = p->stack; |
1285 | } | 1326 | } |
1286 | #endif | 1327 | #endif |
1287 | } /* end of "while we read /proc" */ | 1328 | } /* end of "while we read /proc" */ |
@@ -1310,30 +1351,40 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
1310 | qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); | 1351 | qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); |
1311 | } | 1352 | } |
1312 | #endif | 1353 | #endif |
1313 | IF_FEATURE_TOP_INTERACTIVE(display:) | 1354 | IF_FEATURE_TOP_INTERACTIVE(redraw:) |
1355 | G.lines_remaining = G.lines; | ||
1314 | IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) { | 1356 | IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) { |
1315 | display_process_list(G.lines, col); | 1357 | display_process_list(); |
1316 | } | 1358 | } |
1317 | #if ENABLE_FEATURE_TOPMEM | 1359 | #if ENABLE_FEATURE_TOPMEM |
1318 | else { /* TOPMEM */ | 1360 | else { /* TOPMEM */ |
1319 | display_topmem_process_list(G.lines, col); | 1361 | display_topmem_process_list(); |
1320 | } | 1362 | } |
1321 | #endif | 1363 | #endif |
1364 | print_end(); | ||
1365 | fflush_all(); | ||
1322 | if (iterations >= 0 && !--iterations) | 1366 | if (iterations >= 0 && !--iterations) |
1323 | break; | 1367 | break; |
1324 | #if !ENABLE_FEATURE_TOP_INTERACTIVE | 1368 | #if !ENABLE_FEATURE_TOP_INTERACTIVE |
1325 | clearmems(); | 1369 | clearmems(); |
1326 | sleep_for_duration(interval); | 1370 | sleep_for_duration(interval); |
1327 | #else | 1371 | #else |
1328 | new_mask = handle_input(scan_mask, interval); | 1372 | new_mask = handle_input(scan_mask, |
1329 | if (new_mask == NO_RESCAN_MASK) | 1373 | /* After "redraw with no rescan", have one |
1330 | goto display; | 1374 | * key timeout shorter that normal |
1375 | * (IOW: rescan sooner): | ||
1376 | */ | ||
1377 | (new_mask == ONLY_REDRAW ? 1 : interval) | ||
1378 | ); | ||
1379 | if (new_mask == ONLY_REDRAW) | ||
1380 | goto redraw; | ||
1331 | scan_mask = new_mask; | 1381 | scan_mask = new_mask; |
1332 | clearmems(); | 1382 | clearmems(); |
1333 | #endif | 1383 | #endif |
1334 | } /* end of "while (not Q)" */ | 1384 | } /* end of "while (not Q)" */ |
1335 | 1385 | ||
1336 | bb_putchar('\n'); | 1386 | if (!OPT_BATCH_MODE) |
1387 | bb_putchar('\n'); | ||
1337 | #if ENABLE_FEATURE_TOP_INTERACTIVE | 1388 | #if ENABLE_FEATURE_TOP_INTERACTIVE |
1338 | reset_term(); | 1389 | reset_term(); |
1339 | #endif | 1390 | #endif |