diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-09-08 16:51:19 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-09-08 16:51:19 +0000 |
commit | ff6e8e2974433aeabaaefaf9d8b6a35e9641b9ac (patch) | |
tree | 83de779a734d2a1e6d877718841188e630629265 | |
parent | 9382b3809b3f6bea8dec6483ff66d7c2b21abd94 (diff) | |
download | busybox-w32-ff6e8e2974433aeabaaefaf9d8b6a35e9641b9ac.tar.gz busybox-w32-ff6e8e2974433aeabaaefaf9d8b6a35e9641b9ac.tar.bz2 busybox-w32-ff6e8e2974433aeabaaefaf9d8b6a35e9641b9ac.zip |
top: TOPMEM feature - 's(how sizes)' command. +2.5k when enabled,
+80 bytes when disabled (mainly because of text wrapping fixes
in display_process_list).
-rw-r--r-- | include/libbb.h | 12 | ||||
-rw-r--r-- | libbb/procps.c | 73 | ||||
-rw-r--r-- | procps/Config.in | 7 | ||||
-rw-r--r-- | procps/top.c | 435 |
4 files changed, 473 insertions, 54 deletions
diff --git a/include/libbb.h b/include/libbb.h index f1658945c..a4aa90da3 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -904,6 +904,15 @@ typedef struct procps_status_t { | |||
904 | unsigned uid; | 904 | unsigned uid; |
905 | unsigned gid; | 905 | unsigned gid; |
906 | unsigned tty_major,tty_minor; | 906 | unsigned tty_major,tty_minor; |
907 | #if ENABLE_FEATURE_TOPMEM | ||
908 | unsigned long mapped_rw; | ||
909 | unsigned long mapped_ro; | ||
910 | unsigned long shared_clean; | ||
911 | unsigned long shared_dirty; | ||
912 | unsigned long private_clean; | ||
913 | unsigned long private_dirty; | ||
914 | unsigned long stack; | ||
915 | #endif | ||
907 | char state[4]; | 916 | char state[4]; |
908 | /* basename of executable in exec(2), read from /proc/N/stat | 917 | /* basename of executable in exec(2), read from /proc/N/stat |
909 | * (if executable is symlink or script, it is NOT replaced | 918 | * (if executable is symlink or script, it is NOT replaced |
@@ -927,7 +936,8 @@ enum { | |||
927 | PSSCAN_STIME = 1 << 12, | 936 | PSSCAN_STIME = 1 << 12, |
928 | PSSCAN_UTIME = 1 << 13, | 937 | PSSCAN_UTIME = 1 << 13, |
929 | PSSCAN_TTY = 1 << 14, | 938 | PSSCAN_TTY = 1 << 14, |
930 | USE_SELINUX(PSSCAN_CONTEXT = 1 << 15,) | 939 | PSSCAN_SMAPS = (1 << 15) * ENABLE_FEATURE_TOPMEM, |
940 | USE_SELINUX(PSSCAN_CONTEXT = 1 << 16,) | ||
931 | /* These are all retrieved from proc/NN/stat in one go: */ | 941 | /* These are all retrieved from proc/NN/stat in one go: */ |
932 | PSSCAN_STAT = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID | 942 | PSSCAN_STAT = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID |
933 | | PSSCAN_COMM | PSSCAN_STATE | 943 | | PSSCAN_COMM | PSSCAN_STATE |
diff --git a/libbb/procps.c b/libbb/procps.c index 8d3aea332..3a31eeff3 100644 --- a/libbb/procps.c +++ b/libbb/procps.c | |||
@@ -115,6 +115,28 @@ void free_procps_scan(procps_status_t* sp) | |||
115 | free(sp); | 115 | free(sp); |
116 | } | 116 | } |
117 | 117 | ||
118 | #if ENABLE_FEATURE_TOPMEM | ||
119 | static unsigned long fast_strtoul_16(char **endptr) | ||
120 | { | ||
121 | unsigned char c; | ||
122 | char *str = *endptr; | ||
123 | unsigned long n = 0; | ||
124 | |||
125 | while ((c = *str++) != ' ') { | ||
126 | c = ((c|0x20) - '0'); | ||
127 | if (c > 9) | ||
128 | // c = c + '0' - 'a' + 10: | ||
129 | c = c - ('a' - '0' - 10); | ||
130 | n = n*16 + c; | ||
131 | } | ||
132 | *endptr = str; /* We skip trailing space! */ | ||
133 | return n; | ||
134 | } | ||
135 | /* TOPMEM uses fast_strtoul_10, so... */ | ||
136 | #undef ENABLE_FEATURE_FAST_TOP | ||
137 | #define ENABLE_FEATURE_FAST_TOP 1 | ||
138 | #endif | ||
139 | |||
118 | #if ENABLE_FEATURE_FAST_TOP | 140 | #if ENABLE_FEATURE_FAST_TOP |
119 | /* We cut a lot of corners here for speed */ | 141 | /* We cut a lot of corners here for speed */ |
120 | static unsigned long fast_strtoul_10(char **endptr) | 142 | static unsigned long fast_strtoul_10(char **endptr) |
@@ -278,6 +300,57 @@ procps_status_t *procps_scan(procps_status_t* sp, int flags) | |||
278 | 300 | ||
279 | } | 301 | } |
280 | 302 | ||
303 | #if ENABLE_FEATURE_TOPMEM | ||
304 | if (flags & (PSSCAN_SMAPS)) { | ||
305 | FILE *file; | ||
306 | |||
307 | strcpy(filename_tail, "/smaps"); | ||
308 | file = fopen(filename, "r"); | ||
309 | if (!file) | ||
310 | break; | ||
311 | while (fgets(buf, sizeof(buf), file)) { | ||
312 | unsigned long sz; | ||
313 | char *tp; | ||
314 | char w; | ||
315 | #define SCAN(str, name) \ | ||
316 | if (strncmp(buf, str, sizeof(str)-1) == 0) { \ | ||
317 | tp = skip_whitespace(buf + sizeof(str)-1); \ | ||
318 | sp->name += fast_strtoul_10(&tp); \ | ||
319 | continue; \ | ||
320 | } | ||
321 | SCAN("Shared_Clean:" , shared_clean ); | ||
322 | SCAN("Shared_Dirty:" , shared_dirty ); | ||
323 | SCAN("Private_Clean:", private_clean); | ||
324 | SCAN("Private_Dirty:", private_dirty); | ||
325 | #undef SCAN | ||
326 | // f7d29000-f7d39000 rw-s ADR M:m OFS FILE | ||
327 | tp = strchr(buf, '-'); | ||
328 | if (tp) { | ||
329 | *tp = ' '; | ||
330 | tp = buf; | ||
331 | sz = fast_strtoul_16(&tp); /* start */ | ||
332 | sz = (fast_strtoul_16(&tp) - sz) >> 10; /* end - start */ | ||
333 | // tp -> "rw-s" string | ||
334 | w = tp[1]; | ||
335 | // skipping "rw-s ADR M:m OFS " | ||
336 | tp = skip_whitespace(skip_fields(tp, 4)); | ||
337 | // filter out /dev/something (something != zero) | ||
338 | if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) { | ||
339 | if (w == 'w') { | ||
340 | sp->mapped_rw += sz; | ||
341 | } else if (w == '-') { | ||
342 | sp->mapped_ro += sz; | ||
343 | } | ||
344 | } | ||
345 | //else printf("DROPPING %s (%s)\n", buf, tp); | ||
346 | if (strcmp(tp, "[stack]\n") == 0) | ||
347 | sp->stack += sz; | ||
348 | } | ||
349 | } | ||
350 | fclose(file); | ||
351 | } | ||
352 | #endif /* TOPMEM */ | ||
353 | |||
281 | #if 0 /* PSSCAN_CMD is not used */ | 354 | #if 0 /* PSSCAN_CMD is not used */ |
282 | if (flags & (PSSCAN_CMD|PSSCAN_ARGV0)) { | 355 | if (flags & (PSSCAN_CMD|PSSCAN_ARGV0)) { |
283 | if (sp->argv0) { | 356 | if (sp->argv0) { |
diff --git a/procps/Config.in b/procps/Config.in index f041b5d72..2bd3cd245 100644 --- a/procps/Config.in +++ b/procps/Config.in | |||
@@ -128,6 +128,13 @@ config FEATURE_TOP_DECIMALS | |||
128 | help | 128 | help |
129 | Show 1/10th of a percent in CPU/mem statistics. | 129 | Show 1/10th of a percent in CPU/mem statistics. |
130 | 130 | ||
131 | config FEATURE_TOPMEM | ||
132 | bool "topmem" | ||
133 | default n | ||
134 | depends on TOP | ||
135 | help | ||
136 | Enable 's' in top (gives lots of memory info) | ||
137 | |||
131 | config UPTIME | 138 | config UPTIME |
132 | bool "uptime" | 139 | bool "uptime" |
133 | default n | 140 | default n |
diff --git a/procps/top.c b/procps/top.c index 87e85003d..69f63d08b 100644 --- a/procps/top.c +++ b/procps/top.c | |||
@@ -58,11 +58,17 @@ typedef struct save_hist { | |||
58 | 58 | ||
59 | typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q); | 59 | typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q); |
60 | 60 | ||
61 | |||
61 | enum { SORT_DEPTH = 3 }; | 62 | enum { SORT_DEPTH = 3 }; |
62 | 63 | ||
64 | |||
63 | struct globals { | 65 | struct globals { |
64 | top_status_t *top; | 66 | top_status_t *top; |
65 | int ntop; | 67 | int ntop; |
68 | #if ENABLE_FEATURE_TOPMEM | ||
69 | smallint sort_field; | ||
70 | smallint inverted; | ||
71 | #endif | ||
66 | #if ENABLE_FEATURE_USE_TERMIOS | 72 | #if ENABLE_FEATURE_USE_TERMIOS |
67 | struct termios initial_settings; | 73 | struct termios initial_settings; |
68 | #endif | 74 | #endif |
@@ -81,7 +87,9 @@ struct globals { | |||
81 | #define G (*(struct globals*)&bb_common_bufsiz1) | 87 | #define G (*(struct globals*)&bb_common_bufsiz1) |
82 | #define top (G.top ) | 88 | #define top (G.top ) |
83 | #define ntop (G.ntop ) | 89 | #define ntop (G.ntop ) |
84 | #define initial_settings (G. initial_settings ) | 90 | #define sort_field (G.sort_field ) |
91 | #define inverted (G.inverted ) | ||
92 | #define initial_settings (G.initial_settings ) | ||
85 | #define sort_function (G.sort_function ) | 93 | #define sort_function (G.sort_function ) |
86 | #define prev_hist (G.prev_hist ) | 94 | #define prev_hist (G.prev_hist ) |
87 | #define prev_hist_count (G.prev_hist_count ) | 95 | #define prev_hist_count (G.prev_hist_count ) |
@@ -371,15 +379,11 @@ static void display_process_list(int count, int scr_width) | |||
371 | /* what info of the processes is shown */ | 379 | /* what info of the processes is shown */ |
372 | printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, | 380 | printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, |
373 | " PID PPID USER STAT VSZ %MEM %CPU COMMAND"); | 381 | " PID PPID USER STAT VSZ %MEM %CPU COMMAND"); |
374 | #define MIN_WIDTH \ | ||
375 | sizeof( " PID PPID USER STAT VSZ %MEM %CPU C") | ||
376 | #else | 382 | #else |
377 | 383 | ||
378 | /* !CPU_USAGE_PERCENTAGE */ | 384 | /* !CPU_USAGE_PERCENTAGE */ |
379 | printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, | 385 | printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, |
380 | " PID PPID USER STAT VSZ %MEM COMMAND"); | 386 | " PID PPID USER STAT VSZ %MEM COMMAND"); |
381 | #define MIN_WIDTH \ | ||
382 | sizeof( " PID PPID USER STAT VSZ %MEM C") | ||
383 | #endif | 387 | #endif |
384 | 388 | ||
385 | #if ENABLE_FEATURE_TOP_DECIMALS | 389 | #if ENABLE_FEATURE_TOP_DECIMALS |
@@ -434,20 +438,23 @@ static void display_process_list(int count, int scr_width) | |||
434 | /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ | 438 | /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ |
435 | #endif | 439 | #endif |
436 | 440 | ||
437 | /* Ok, all prelim data is ready, go thru the list */ | 441 | scr_width += 2; /* account for leading '\n' and trailing NUL */ |
442 | /* Ok, all preliminary data is ready, go thru the list */ | ||
438 | while (count-- > 0) { | 443 | while (count-- > 0) { |
439 | int col = scr_width; | 444 | char buf[scr_width]; |
445 | unsigned col; | ||
440 | CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift); | 446 | CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift); |
441 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 447 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
442 | CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift); | 448 | CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift); |
443 | #endif | 449 | #endif |
444 | 450 | ||
445 | if (s->vsz >= 100*1024) | 451 | if (s->vsz >= 100000) |
446 | sprintf(vsz_str_buf, "%6ldm", s->vsz/1024); | 452 | sprintf(vsz_str_buf, "%6ldm", s->vsz/1024); |
447 | else | 453 | else |
448 | sprintf(vsz_str_buf, "%7ld", s->vsz); | 454 | sprintf(vsz_str_buf, "%7ld", s->vsz); |
449 | // PID PPID USER STAT VSZ %MEM [%CPU] COMMAND | 455 | // PID PPID USER STAT VSZ %MEM [%CPU] COMMAND |
450 | col -= printf("\n" "%5u%6u %-8.8s %s%s" FMT | 456 | col = snprintf(buf, scr_width, |
457 | "\n" "%5u%6u %-8.8s %s%s" FMT | ||
451 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 458 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
452 | FMT | 459 | FMT |
453 | #endif | 460 | #endif |
@@ -459,11 +466,9 @@ static void display_process_list(int count, int scr_width) | |||
459 | , SHOW_STAT(pcpu) | 466 | , SHOW_STAT(pcpu) |
460 | #endif | 467 | #endif |
461 | ); | 468 | ); |
462 | if (col > 0) { | 469 | if (col < scr_width) |
463 | char buf[col + 1]; | 470 | read_cmdline(buf + col, scr_width - col, s->pid, s->comm); |
464 | read_cmdline(buf, col, s->pid, s->comm); | 471 | fputs(buf, stdout); |
465 | fputs(buf, stdout); | ||
466 | } | ||
467 | /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu, | 472 | /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu, |
468 | jif.busy - prev_jif.busy, jif.total - prev_jif.total); */ | 473 | jif.busy - prev_jif.busy, jif.total - prev_jif.total); */ |
469 | s++; | 474 | s++; |
@@ -481,7 +486,7 @@ static void clearmems(void) | |||
481 | { | 486 | { |
482 | clear_username_cache(); | 487 | clear_username_cache(); |
483 | free(top); | 488 | free(top); |
484 | top = 0; | 489 | top = NULL; |
485 | ntop = 0; | 490 | ntop = 0; |
486 | } | 491 | } |
487 | 492 | ||
@@ -508,13 +513,298 @@ static void sig_catcher(int sig ATTRIBUTE_UNUSED) | |||
508 | #endif /* FEATURE_USE_TERMIOS */ | 513 | #endif /* FEATURE_USE_TERMIOS */ |
509 | 514 | ||
510 | 515 | ||
516 | |||
517 | |||
518 | |||
519 | |||
520 | |||
521 | |||
522 | |||
523 | |||
524 | |||
525 | |||
526 | |||
527 | |||
528 | |||
529 | |||
530 | typedef unsigned long mem_t; | ||
531 | |||
532 | typedef struct topmem_status_t { | ||
533 | unsigned pid; | ||
534 | char comm[COMM_LEN]; | ||
535 | /* vsz doesn't count /dev/xxx mappings except /dev/zero */ | ||
536 | mem_t vsz ; | ||
537 | mem_t vszrw ; | ||
538 | mem_t rss ; | ||
539 | mem_t rss_sh ; | ||
540 | mem_t dirty ; | ||
541 | mem_t dirty_sh; | ||
542 | mem_t stack ; | ||
543 | } topmem_status_t; | ||
544 | |||
545 | enum { NUM_SORT_FIELD = 7 }; | ||
546 | |||
547 | #define topmem ((topmem_status_t*)top) | ||
548 | |||
549 | #if ENABLE_FEATURE_TOPMEM | ||
550 | static int topmem_sort(char *a, char *b) | ||
551 | { | ||
552 | int n; | ||
553 | mem_t l, r; | ||
554 | |||
555 | n = offsetof(topmem_status_t, vsz) + (sort_field * sizeof(mem_t)); | ||
556 | l = *(mem_t*)(a + n); | ||
557 | r = *(mem_t*)(b + n); | ||
558 | // if (l == r) { | ||
559 | // l = a->mapped_rw; | ||
560 | // r = b->mapped_rw; | ||
561 | // } | ||
562 | /* We want to avoid unsigned->signed and truncation errors */ | ||
563 | /* l>r: -1, l=r: 0, l<r: 1 */ | ||
564 | n = (l > r) ? -1 : (l != r); | ||
565 | return inverted ? -n : n; | ||
566 | } | ||
567 | |||
568 | /* Cut "NNNN " out of " NNNN kb" */ | ||
569 | static char *grab_number(char *str, const char *match, unsigned sz) | ||
570 | { | ||
571 | if (strncmp(str, match, sz) == 0) { | ||
572 | str = skip_whitespace(str + sz); | ||
573 | (skip_non_whitespace(str))[1] = '\0'; | ||
574 | return xstrdup(str); | ||
575 | } | ||
576 | return NULL; | ||
577 | } | ||
578 | |||
579 | /* display header info (meminfo / loadavg) */ | ||
580 | static void display_topmem_header(int scr_width) | ||
581 | { | ||
582 | char linebuf[128]; | ||
583 | int i; | ||
584 | FILE *fp; | ||
585 | union { | ||
586 | struct { | ||
587 | /* 1 */ char *total; | ||
588 | /* 2 */ char *mfree; | ||
589 | /* 3 */ char *buf; | ||
590 | /* 4 */ char *cache; | ||
591 | /* 5 */ char *swaptotal; | ||
592 | /* 6 */ char *swapfree; | ||
593 | /* 7 */ char *dirty; | ||
594 | /* 8 */ char *mwrite; | ||
595 | /* 9 */ char *anon; | ||
596 | /* 10 */ char *map; | ||
597 | /* 11 */ char *slab; | ||
598 | }; | ||
599 | char *str[11]; | ||
600 | } Z; | ||
601 | #define total Z.total | ||
602 | #define mfree Z.mfree | ||
603 | #define buf Z.buf | ||
604 | #define cache Z.cache | ||
605 | #define swaptotal Z.swaptotal | ||
606 | #define swapfree Z.swapfree | ||
607 | #define dirty Z.dirty | ||
608 | #define mwrite Z.mwrite | ||
609 | #define anon Z.anon | ||
610 | #define map Z.map | ||
611 | #define slab Z.slab | ||
612 | #define str Z.str | ||
613 | |||
614 | memset(&Z, 0, sizeof(Z)); | ||
615 | |||
616 | /* read memory info */ | ||
617 | fp = xfopen("meminfo", "r"); | ||
618 | while (fgets(linebuf, sizeof(linebuf), fp)) { | ||
619 | char *p; | ||
620 | |||
621 | #define SCAN(match, name) \ | ||
622 | p = grab_number(linebuf, match, sizeof(match)-1); \ | ||
623 | if (p) { name = p; continue; } | ||
624 | |||
625 | SCAN("MemTotal:", total); | ||
626 | SCAN("MemFree:", mfree); | ||
627 | SCAN("Buffers:", buf); | ||
628 | SCAN("Cached:", cache); | ||
629 | SCAN("SwapTotal:", swaptotal); | ||
630 | SCAN("SwapFree:", swapfree); | ||
631 | SCAN("Dirty:", dirty); | ||
632 | SCAN("Writeback:", mwrite); | ||
633 | SCAN("AnonPages:", anon); | ||
634 | SCAN("Mapped:", map); | ||
635 | SCAN("Slab:", slab); | ||
636 | #undef SCAN | ||
637 | } | ||
638 | fclose(fp); | ||
639 | |||
640 | #define S(s) (s ? s : "0") | ||
641 | snprintf(linebuf, sizeof(linebuf), | ||
642 | "Mem %stotal %sanon %smap %sfree", | ||
643 | S(total), S(anon), S(map), S(mfree)); | ||
644 | printf(OPT_BATCH_MODE ? "%.*s\n" : "\e[H\e[J%.*s\n", scr_width, linebuf); | ||
645 | |||
646 | snprintf(linebuf, sizeof(linebuf), | ||
647 | " %sslab %sbuf %scache %sdirty %swrite", | ||
648 | S(slab), S(buf), S(cache), S(dirty), S(mwrite)); | ||
649 | printf("%.*s\n", scr_width, linebuf); | ||
650 | |||
651 | snprintf(linebuf, sizeof(linebuf), | ||
652 | "Swap %stotal %sfree", // TODO: % used? | ||
653 | S(swaptotal), S(swapfree)); | ||
654 | printf("%.*s\n", scr_width, linebuf); | ||
655 | #undef S | ||
656 | |||
657 | for (i = 0; i < ARRAY_SIZE(str); i++) | ||
658 | free(str[i]); | ||
659 | #undef total | ||
660 | #undef free | ||
661 | #undef buf | ||
662 | #undef cache | ||
663 | #undef swaptotal | ||
664 | #undef swapfree | ||
665 | #undef dirty | ||
666 | #undef write | ||
667 | #undef anon | ||
668 | #undef map | ||
669 | #undef slab | ||
670 | #undef str | ||
671 | } | ||
672 | |||
673 | // Converts unsigned long long value into compact 5-char | ||
674 | // representation. Sixth char is always ' ' | ||
675 | static void smart_ulltoa6(unsigned long long ul, char buf[6]) | ||
676 | { | ||
677 | const char *fmt; | ||
678 | char c; | ||
679 | unsigned v, u, idx = 0; | ||
680 | |||
681 | if (ul > 99999) { // do not scale if 99999 or less | ||
682 | ul *= 10; | ||
683 | do { | ||
684 | ul /= 1024; | ||
685 | idx++; | ||
686 | } while (ul >= 100000); | ||
687 | } | ||
688 | v = ul; // ullong divisions are expensive, avoid them | ||
689 | |||
690 | fmt = " 123456789"; | ||
691 | u = v / 10; | ||
692 | v = v % 10; | ||
693 | if (!idx) { | ||
694 | // 99999 or less: use "12345" format | ||
695 | // u is value/10, v is last digit | ||
696 | c = buf[0] = " 123456789"[u/1000]; | ||
697 | if (c != ' ') fmt = "0123456789"; | ||
698 | c = buf[1] = fmt[u/100%10]; | ||
699 | if (c != ' ') fmt = "0123456789"; | ||
700 | c = buf[2] = fmt[u/10%10]; | ||
701 | if (c != ' ') fmt = "0123456789"; | ||
702 | buf[3] = fmt[u%10]; | ||
703 | buf[4] = "0123456789"[v]; | ||
704 | } else { | ||
705 | // value has been scaled into 0..9999.9 range | ||
706 | // u is value, v is 1/10ths (allows for 92.1M format) | ||
707 | if (u >= 100) { | ||
708 | // value is >= 100: use "1234M', " 123M" formats | ||
709 | c = buf[0] = " 123456789"[u/1000]; | ||
710 | if (c != ' ') fmt = "0123456789"; | ||
711 | c = buf[1] = fmt[u/100%10]; | ||
712 | if (c != ' ') fmt = "0123456789"; | ||
713 | v = u % 10; | ||
714 | u = u / 10; | ||
715 | buf[2] = fmt[u%10]; | ||
716 | } else { | ||
717 | // value is < 100: use "92.1M" format | ||
718 | c = buf[0] = " 123456789"[u/10]; | ||
719 | if (c != ' ') fmt = "0123456789"; | ||
720 | buf[1] = fmt[u%10]; | ||
721 | buf[2] = '.'; | ||
722 | } | ||
723 | buf[3] = "0123456789"[v]; | ||
724 | // see http://en.wikipedia.org/wiki/Tera | ||
725 | buf[4] = " mgtpezy"[idx]; | ||
726 | } | ||
727 | buf[5] = ' '; | ||
728 | } | ||
729 | |||
730 | static void display_topmem_process_list(int count, int scr_width) | ||
731 | { | ||
732 | #define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK" | ||
733 | #define MIN_WIDTH sizeof(HDR_STR) | ||
734 | const topmem_status_t *s = topmem; | ||
735 | char buf[scr_width | MIN_WIDTH]; /* a|b is a cheap max(a,b) */ | ||
736 | |||
737 | display_topmem_header(scr_width); | ||
738 | strcpy(buf, HDR_STR " COMMAND"); | ||
739 | buf[5 + sort_field * 6] = '*'; | ||
740 | printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, buf); | ||
741 | |||
742 | while (--count >= 0) { | ||
743 | // PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND | ||
744 | smart_ulltoa6(s->pid , &buf[0*6]); | ||
745 | smart_ulltoa6(s->vsz , &buf[1*6]); | ||
746 | smart_ulltoa6(s->vszrw , &buf[2*6]); | ||
747 | smart_ulltoa6(s->rss , &buf[3*6]); | ||
748 | smart_ulltoa6(s->rss_sh , &buf[4*6]); | ||
749 | smart_ulltoa6(s->dirty , &buf[5*6]); | ||
750 | smart_ulltoa6(s->dirty_sh, &buf[6*6]); | ||
751 | smart_ulltoa6(s->stack , &buf[7*6]); | ||
752 | buf[8*6] = '\0'; | ||
753 | if (scr_width > MIN_WIDTH) { | ||
754 | read_cmdline(&buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm); | ||
755 | } | ||
756 | printf("\n""%.*s", scr_width, buf); | ||
757 | s++; | ||
758 | } | ||
759 | putchar(OPT_BATCH_MODE ? '\n' : '\r'); | ||
760 | fflush(stdout); | ||
761 | #undef HDR_STR | ||
762 | #undef MIN_WIDTH | ||
763 | } | ||
764 | #else | ||
765 | void display_topmem_process_list(int count, int scr_width); | ||
766 | int topmem_sort(char *a, char *b); | ||
767 | #endif /* TOPMEM */ | ||
768 | |||
769 | |||
770 | |||
771 | |||
772 | |||
773 | |||
774 | |||
775 | |||
776 | |||
777 | |||
778 | |||
779 | |||
780 | |||
781 | |||
782 | |||
783 | |||
784 | enum { | ||
785 | TOP_MASK = 0 | ||
786 | | PSSCAN_PID | ||
787 | | PSSCAN_PPID | ||
788 | | PSSCAN_VSZ | ||
789 | | PSSCAN_STIME | ||
790 | | PSSCAN_UTIME | ||
791 | | PSSCAN_STATE | ||
792 | | PSSCAN_COMM | ||
793 | | PSSCAN_UIDGID, | ||
794 | TOPMEM_MASK = 0 | ||
795 | | PSSCAN_PID | ||
796 | | PSSCAN_SMAPS | ||
797 | | PSSCAN_COMM, | ||
798 | }; | ||
799 | |||
511 | int top_main(int argc, char **argv); | 800 | int top_main(int argc, char **argv); |
512 | int top_main(int argc, char **argv) | 801 | int top_main(int argc, char **argv) |
513 | { | 802 | { |
514 | int count, lines, col; | 803 | int count, lines, col; |
515 | unsigned interval; | 804 | unsigned interval; |
516 | int iterations = -1; /* infinite */ | 805 | int iterations = 0; /* infinite */ |
517 | char *sinterval, *siterations; | 806 | char *sinterval, *siterations; |
807 | SKIP_FEATURE_TOPMEM(const) unsigned scan_mask = TOP_MASK; | ||
518 | #if ENABLE_FEATURE_USE_TERMIOS | 808 | #if ENABLE_FEATURE_USE_TERMIOS |
519 | struct termios new_settings; | 809 | struct termios new_settings; |
520 | struct pollfd pfd[1]; | 810 | struct pollfd pfd[1]; |
@@ -563,62 +853,82 @@ int top_main(int argc, char **argv) | |||
563 | procps_status_t *p = NULL; | 853 | procps_status_t *p = NULL; |
564 | 854 | ||
565 | /* Default */ | 855 | /* Default */ |
566 | lines = 24 - 3 USE_FEATURE_TOP_CPU_GLOBAL_PERCENTS( - 1); | 856 | lines = 24; |
567 | col = 79; | 857 | col = 79; |
568 | #if ENABLE_FEATURE_USE_TERMIOS | 858 | #if ENABLE_FEATURE_USE_TERMIOS |
569 | get_terminal_width_height(0, &col, &lines); | 859 | get_terminal_width_height(0, &col, &lines); |
570 | /* We wrap horribly if width is too narrow (TODO) */ | 860 | if (lines < 5 || col < 10) { |
571 | if (lines < 5 || col < MIN_WIDTH) { | ||
572 | sleep(interval); | 861 | sleep(interval); |
573 | continue; | 862 | continue; |
574 | } | 863 | } |
575 | lines -= 3 USE_FEATURE_TOP_CPU_GLOBAL_PERCENTS( + 1); | ||
576 | #endif /* FEATURE_USE_TERMIOS */ | 864 | #endif /* FEATURE_USE_TERMIOS */ |
865 | if (!ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && scan_mask == TOP_MASK) | ||
866 | lines -= 3; | ||
867 | else | ||
868 | lines -= 4; | ||
577 | 869 | ||
578 | /* read process IDs & status for all the processes */ | 870 | /* read process IDs & status for all the processes */ |
579 | while ((p = procps_scan(p, 0 | 871 | while ((p = procps_scan(p, scan_mask)) != NULL) { |
580 | | PSSCAN_PID | 872 | int n; |
581 | | PSSCAN_PPID | 873 | if (scan_mask == TOP_MASK) { |
582 | | PSSCAN_VSZ | 874 | n = ntop; |
583 | | PSSCAN_STIME | 875 | top = xrealloc(top, (++ntop) * sizeof(*top)); |
584 | | PSSCAN_UTIME | 876 | top[n].pid = p->pid; |
585 | | PSSCAN_STATE | 877 | top[n].ppid = p->ppid; |
586 | | PSSCAN_COMM | 878 | top[n].vsz = p->vsz; |
587 | | PSSCAN_UIDGID | ||
588 | )) != NULL) { | ||
589 | int n = ntop; | ||
590 | top = xrealloc(top, (++ntop) * sizeof(*top)); | ||
591 | top[n].pid = p->pid; | ||
592 | top[n].ppid = p->ppid; | ||
593 | top[n].vsz = p->vsz; | ||
594 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 879 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
595 | top[n].ticks = p->stime + p->utime; | 880 | top[n].ticks = p->stime + p->utime; |
596 | #endif | 881 | #endif |
597 | top[n].uid = p->uid; | 882 | top[n].uid = p->uid; |
598 | strcpy(top[n].state, p->state); | 883 | strcpy(top[n].state, p->state); |
599 | strcpy(top[n].comm, p->comm); | 884 | strcpy(top[n].comm, p->comm); |
885 | } else { /* TOPMEM */ | ||
886 | #if ENABLE_FEATURE_TOPMEM | ||
887 | if (!(p->mapped_ro | p->mapped_rw)) | ||
888 | continue; /* kernel threads are ignored */ | ||
889 | n = ntop; | ||
890 | top = xrealloc(topmem, (++ntop) * sizeof(*topmem)); | ||
891 | strcpy(topmem[n].comm, p->comm); | ||
892 | topmem[n].pid = p->pid; | ||
893 | topmem[n].vsz = p->mapped_rw + p->mapped_ro; | ||
894 | topmem[n].vszrw = p->mapped_rw; | ||
895 | topmem[n].rss_sh = p->shared_clean + p->shared_dirty; | ||
896 | topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh; | ||
897 | topmem[n].dirty = p->private_dirty + p->shared_dirty; | ||
898 | topmem[n].dirty_sh = p->shared_dirty; | ||
899 | topmem[n].stack = p->stack; | ||
900 | #endif | ||
901 | } | ||
600 | } | 902 | } |
601 | if (ntop == 0) { | 903 | if (ntop == 0) { |
602 | bb_error_msg_and_die("no process info in /proc"); | 904 | bb_error_msg_and_die("no process info in /proc"); |
603 | } | 905 | } |
906 | |||
907 | if (scan_mask == TOP_MASK) { | ||
604 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 908 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
605 | if (!prev_hist_count) { | 909 | if (!prev_hist_count) { |
910 | do_stats(); | ||
911 | usleep(100000); | ||
912 | clearmems(); | ||
913 | continue; | ||
914 | } | ||
606 | do_stats(); | 915 | do_stats(); |
607 | sleep(1); | ||
608 | clearmems(); | ||
609 | continue; | ||
610 | } | ||
611 | do_stats(); | ||
612 | /* TODO: we don't need to sort all 10000 processes, we need to find top 24! */ | 916 | /* TODO: we don't need to sort all 10000 processes, we need to find top 24! */ |
613 | qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp); | 917 | qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp); |
614 | #else | 918 | #else |
615 | qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0])); | 919 | qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0])); |
616 | #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ | 920 | #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ |
921 | } else { /* TOPMEM */ | ||
922 | qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); | ||
923 | } | ||
617 | count = lines; | 924 | count = lines; |
618 | if (OPT_BATCH_MODE || count > ntop) { | 925 | if (OPT_BATCH_MODE || count > ntop) { |
619 | count = ntop; | 926 | count = ntop; |
620 | } | 927 | } |
621 | display_process_list(count, col); | 928 | if (scan_mask == TOP_MASK) |
929 | display_process_list(count, col); | ||
930 | else | ||
931 | display_topmem_process_list(count, col); | ||
622 | clearmems(); | 932 | clearmems(); |
623 | if (iterations >= 0 && !--iterations) | 933 | if (iterations >= 0 && !--iterations) |
624 | break; | 934 | break; |
@@ -628,11 +938,17 @@ int top_main(int argc, char **argv) | |||
628 | if (poll(pfd, 1, interval * 1000) != 0) { | 938 | if (poll(pfd, 1, interval * 1000) != 0) { |
629 | if (read(0, &c, 1) != 1) /* signal */ | 939 | if (read(0, &c, 1) != 1) /* signal */ |
630 | break; | 940 | break; |
631 | if (c == 'q' || c == initial_settings.c_cc[VINTR]) | 941 | if (c == initial_settings.c_cc[VINTR]) |
942 | break; | ||
943 | c |= 0x20; /* lowercase */ | ||
944 | if (c == 'q') | ||
632 | break; | 945 | break; |
633 | if (c == 'N') | 946 | if (c == 'n') { |
947 | USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;) | ||
634 | sort_function[0] = pid_sort; | 948 | sort_function[0] = pid_sort; |
635 | if (c == 'M') { | 949 | } |
950 | if (c == 'm') { | ||
951 | USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;) | ||
636 | sort_function[0] = mem_sort; | 952 | sort_function[0] = mem_sort; |
637 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 953 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
638 | sort_function[1] = pcpu_sort; | 954 | sort_function[1] = pcpu_sort; |
@@ -640,16 +956,29 @@ int top_main(int argc, char **argv) | |||
640 | #endif | 956 | #endif |
641 | } | 957 | } |
642 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 958 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
643 | if (c == 'P') { | 959 | if (c == 'p') { |
960 | USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;) | ||
644 | sort_function[0] = pcpu_sort; | 961 | sort_function[0] = pcpu_sort; |
645 | sort_function[1] = mem_sort; | 962 | sort_function[1] = mem_sort; |
646 | sort_function[2] = time_sort; | 963 | sort_function[2] = time_sort; |
647 | } | 964 | } |
648 | if (c == 'T') { | 965 | if (c == 't') { |
966 | USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;) | ||
649 | sort_function[0] = time_sort; | 967 | sort_function[0] = time_sort; |
650 | sort_function[1] = mem_sort; | 968 | sort_function[1] = mem_sort; |
651 | sort_function[2] = pcpu_sort; | 969 | sort_function[2] = pcpu_sort; |
652 | } | 970 | } |
971 | #if ENABLE_FEATURE_TOPMEM | ||
972 | if (c == 's') { | ||
973 | scan_mask = TOPMEM_MASK; | ||
974 | free(prev_hist); | ||
975 | prev_hist = NULL; | ||
976 | prev_hist_count = 0; | ||
977 | sort_field = (sort_field + 1) % NUM_SORT_FIELD; | ||
978 | } | ||
979 | if (c == 'r') | ||
980 | inverted ^= 1; | ||
981 | #endif | ||
653 | #endif | 982 | #endif |
654 | } | 983 | } |
655 | #endif /* FEATURE_USE_TERMIOS */ | 984 | #endif /* FEATURE_USE_TERMIOS */ |