aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-09-08 16:51:19 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-09-08 16:51:19 +0000
commitff6e8e2974433aeabaaefaf9d8b6a35e9641b9ac (patch)
tree83de779a734d2a1e6d877718841188e630629265
parent9382b3809b3f6bea8dec6483ff66d7c2b21abd94 (diff)
downloadbusybox-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.h12
-rw-r--r--libbb/procps.c73
-rw-r--r--procps/Config.in7
-rw-r--r--procps/top.c435
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
119static 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 */
120static unsigned long fast_strtoul_10(char **endptr) 142static 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
131config 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
131config UPTIME 138config 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
59typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q); 59typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
60 60
61
61enum { SORT_DEPTH = 3 }; 62enum { SORT_DEPTH = 3 };
62 63
64
63struct globals { 65struct 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
530typedef unsigned long mem_t;
531
532typedef 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
545enum { NUM_SORT_FIELD = 7 };
546
547#define topmem ((topmem_status_t*)top)
548
549#if ENABLE_FEATURE_TOPMEM
550static 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" */
569static 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) */
580static 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 ' '
675static 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
730static 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
765void display_topmem_process_list(int count, int scr_width);
766int 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
784enum {
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
511int top_main(int argc, char **argv); 800int top_main(int argc, char **argv);
512int top_main(int argc, char **argv) 801int 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 */