diff options
-rw-r--r-- | procps/top.c | 196 |
1 files changed, 86 insertions, 110 deletions
diff --git a/procps/top.c b/procps/top.c index 530f45fa1..62f9421af 100644 --- a/procps/top.c +++ b/procps/top.c | |||
@@ -499,85 +499,94 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) | |||
499 | # define display_cpus(scr_width, scrbuf, lines_rem) ((void)0) | 499 | # define display_cpus(scr_width, scrbuf, lines_rem) ((void)0) |
500 | #endif | 500 | #endif |
501 | 501 | ||
502 | static unsigned long display_header(int scr_width, int *lines_rem_p) | 502 | enum { |
503 | { | 503 | MI_MEMTOTAL, |
504 | FILE *fp; | 504 | MI_MEMFREE, |
505 | char buf[80]; | 505 | MI_MEMSHARED, |
506 | char scrbuf[80]; | 506 | MI_SHMEM, |
507 | unsigned long total, used, mfree, shared, buffers, cached; | 507 | MI_BUFFERS, |
508 | MI_CACHED, | ||
509 | MI_SWAPTOTAL, | ||
510 | MI_SWAPFREE, | ||
511 | MI_DIRTY, | ||
512 | MI_WRITEBACK, | ||
513 | MI_ANONPAGES, | ||
514 | MI_MAPPED, | ||
515 | MI_SLAB, | ||
516 | MI_MAX | ||
517 | }; | ||
508 | 518 | ||
509 | /* read memory info */ | 519 | static void parse_meminfo(unsigned long meminfo[MI_MAX]) |
510 | fp = xfopen_for_read("meminfo"); | 520 | { |
521 | static const char fields[] = | ||
522 | "MemTotal\0" | ||
523 | "MemFree\0" | ||
524 | "MemShared\0" | ||
525 | "Shmem\0" | ||
526 | "Buffers\0" | ||
527 | "Cached\0" | ||
528 | "SwapTotal\0" | ||
529 | "SwapFree\0" | ||
530 | "Dirty\0" | ||
531 | "Writeback\0" | ||
532 | "AnonPages\0" | ||
533 | "Mapped\0" | ||
534 | "Slab\0"; | ||
535 | char buf[60]; /* actual lines we expect are ~30 chars or less */ | ||
536 | FILE *f; | ||
537 | int i; | ||
511 | 538 | ||
512 | /* | 539 | memset(meminfo, 0, sizeof(meminfo)); |
513 | * Old kernels (such as 2.4.x) had a nice summary of memory info that | 540 | f = xfopen_for_read("meminfo"); |
514 | * we could parse, however this is gone entirely in 2.6. Try parsing | 541 | while (fgets(buf, sizeof(buf), f) != NULL) { |
515 | * the old way first, and if that fails, parse each field manually. | 542 | char *c = strchr(buf, ':'); |
516 | * | 543 | if (!c) |
517 | * First, we read in the first line. Old kernels will have bogus | 544 | continue; |
518 | * strings we don't care about, whereas new kernels will start right | 545 | *c = '\0'; |
519 | * out with MemTotal: | 546 | i = index_in_strings(fields, buf); |
520 | * -- PFM. | 547 | if (i >= 0) |
521 | */ | 548 | meminfo[i] = strtoul(c+1, NULL, 10); |
522 | if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) { | 549 | } |
523 | fgets(buf, sizeof(buf), fp); /* skip first line */ | 550 | fclose(f); |
524 | 551 | } | |
525 | fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu", | ||
526 | &total, &used, &mfree, &shared, &buffers, &cached); | ||
527 | /* convert to kilobytes */ | ||
528 | used /= 1024; | ||
529 | mfree /= 1024; | ||
530 | shared /= 1024; | ||
531 | buffers /= 1024; | ||
532 | cached /= 1024; | ||
533 | total /= 1024; | ||
534 | } else { | ||
535 | /* | ||
536 | * Revert to manual parsing, which incidentally already has the | ||
537 | * sizes in kilobytes. This should be safe for both 2.4 and | ||
538 | * 2.6. | ||
539 | */ | ||
540 | fscanf(fp, "MemFree: %lu %s\n", &mfree, buf); | ||
541 | 552 | ||
542 | /* | ||
543 | * MemShared: is no longer present in 2.6. Report this as 0, | ||
544 | * to maintain consistent behavior with normal procps. | ||
545 | */ | ||
546 | if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2) | ||
547 | shared = 0; | ||
548 | 553 | ||
549 | fscanf(fp, "Buffers: %lu %s\n", &buffers, buf); | 554 | static unsigned long display_header(int scr_width, int *lines_rem_p) |
550 | fscanf(fp, "Cached: %lu %s\n", &cached, buf); | 555 | { |
556 | char scrbuf[100]; /* [80] was a bit too low on 8Gb ram box */ | ||
557 | char *buf; | ||
558 | unsigned long meminfo[MI_MAX]; | ||
551 | 559 | ||
552 | used = total - mfree; | 560 | parse_meminfo(meminfo); |
553 | } | ||
554 | fclose(fp); | ||
555 | 561 | ||
556 | /* output memory info */ | 562 | /* Output memory info */ |
557 | if (scr_width > (int)sizeof(scrbuf)) | 563 | if (scr_width > (int)sizeof(scrbuf)) |
558 | scr_width = sizeof(scrbuf); | 564 | scr_width = sizeof(scrbuf); |
559 | snprintf(scrbuf, scr_width, | 565 | snprintf(scrbuf, scr_width, |
560 | "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", | 566 | "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", |
561 | used, mfree, shared, buffers, cached); | 567 | meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE], |
562 | /* go to top & clear to the end of screen */ | 568 | meminfo[MI_MEMFREE], |
569 | meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM], | ||
570 | meminfo[MI_BUFFERS], | ||
571 | meminfo[MI_CACHED]); | ||
572 | /* Go to top & clear to the end of screen */ | ||
563 | printf(OPT_BATCH_MODE ? "%s\n" : "\033[H\033[J%s\n", scrbuf); | 573 | printf(OPT_BATCH_MODE ? "%s\n" : "\033[H\033[J%s\n", scrbuf); |
564 | (*lines_rem_p)--; | 574 | (*lines_rem_p)--; |
565 | 575 | ||
566 | /* Display CPU time split as percentage of total time | 576 | /* Display CPU time split as percentage of total time. |
567 | * This displays either a cumulative line or one line per CPU | 577 | * This displays either a cumulative line or one line per CPU. |
568 | */ | 578 | */ |
569 | display_cpus(scr_width, scrbuf, lines_rem_p); | 579 | display_cpus(scr_width, scrbuf, lines_rem_p); |
570 | 580 | ||
571 | /* read load average as a string */ | 581 | /* Read load average as a string */ |
572 | buf[0] = '\0'; | 582 | buf = stpcpy(scrbuf, "Load average: "); |
573 | open_read_close("loadavg", buf, sizeof(buf) - 1); | 583 | open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: ")); |
574 | buf[sizeof(buf) - 1] = '\n'; | 584 | scrbuf[scr_width - 1] = '\0'; |
575 | *strchr(buf, '\n') = '\0'; | 585 | strchrnul(buf, '\n')[0] = '\0'; |
576 | snprintf(scrbuf, scr_width, "Load average: %s", buf); | ||
577 | puts(scrbuf); | 586 | puts(scrbuf); |
578 | (*lines_rem_p)--; | 587 | (*lines_rem_p)--; |
579 | 588 | ||
580 | return total; | 589 | return meminfo[MI_MEMTOTAL]; |
581 | } | 590 | } |
582 | 591 | ||
583 | static NOINLINE void display_process_list(int lines_rem, int scr_width) | 592 | static NOINLINE void display_process_list(int lines_rem, int scr_width) |
@@ -781,64 +790,31 @@ static int topmem_sort(char *a, char *b) | |||
781 | /* display header info (meminfo / loadavg) */ | 790 | /* display header info (meminfo / loadavg) */ |
782 | static void display_topmem_header(int scr_width, int *lines_rem_p) | 791 | static void display_topmem_header(int scr_width, int *lines_rem_p) |
783 | { | 792 | { |
784 | enum { | 793 | unsigned long meminfo[MI_MAX]; |
785 | TOTAL = 0, MFREE, BUF, CACHE, | 794 | |
786 | SWAPTOTAL, SWAPFREE, DIRTY, | 795 | parse_meminfo(meminfo); |
787 | MWRITE, ANON, MAP, SLAB, | ||
788 | NUM_FIELDS | ||
789 | }; | ||
790 | static const char match[NUM_FIELDS][12] = { | ||
791 | "\x09" "MemTotal:", // TOTAL | ||
792 | "\x08" "MemFree:", // MFREE | ||
793 | "\x08" "Buffers:", // BUF | ||
794 | "\x07" "Cached:", // CACHE | ||
795 | "\x0a" "SwapTotal:", // SWAPTOTAL | ||
796 | "\x09" "SwapFree:", // SWAPFREE | ||
797 | "\x06" "Dirty:", // DIRTY | ||
798 | "\x0a" "Writeback:", // MWRITE | ||
799 | "\x0a" "AnonPages:", // ANON | ||
800 | "\x07" "Mapped:", // MAP | ||
801 | "\x05" "Slab:", // SLAB | ||
802 | }; | ||
803 | char meminfo_buf[4 * 1024]; | ||
804 | const char *Z[NUM_FIELDS]; | ||
805 | unsigned i; | ||
806 | int sz; | ||
807 | |||
808 | for (i = 0; i < NUM_FIELDS; i++) | ||
809 | Z[i] = "?"; | ||
810 | |||
811 | /* read memory info */ | ||
812 | sz = open_read_close("meminfo", meminfo_buf, sizeof(meminfo_buf) - 1); | ||
813 | if (sz >= 0) { | ||
814 | char *p = meminfo_buf; | ||
815 | meminfo_buf[sz] = '\0'; | ||
816 | /* Note that fields always appear in the match[] order */ | ||
817 | for (i = 0; i < NUM_FIELDS; i++) { | ||
818 | char *found = strstr(p, match[i] + 1); | ||
819 | if (found) { | ||
820 | /* Cut "NNNN" out of " NNNN kb" */ | ||
821 | char *s = skip_whitespace(found + match[i][0]); | ||
822 | p = skip_non_whitespace(s); | ||
823 | *p++ = '\0'; | ||
824 | Z[i] = s; | ||
825 | } | ||
826 | } | ||
827 | } | ||
828 | 796 | ||
829 | snprintf(line_buf, LINE_BUF_SIZE, | 797 | snprintf(line_buf, LINE_BUF_SIZE, |
830 | "Mem total:%s anon:%s map:%s free:%s", | 798 | "Mem total:%lu anon:%lu map:%lu free:%lu", |
831 | Z[TOTAL], Z[ANON], Z[MAP], Z[MFREE]); | 799 | meminfo[MI_MEMTOTAL], |
800 | meminfo[MI_ANONPAGES], | ||
801 | meminfo[MI_MAPPED], | ||
802 | meminfo[MI_MEMFREE]); | ||
832 | printf(OPT_BATCH_MODE ? "%.*s\n" : "\033[H\033[J%.*s\n", scr_width, line_buf); | 803 | printf(OPT_BATCH_MODE ? "%.*s\n" : "\033[H\033[J%.*s\n", scr_width, line_buf); |
833 | 804 | ||
834 | snprintf(line_buf, LINE_BUF_SIZE, | 805 | snprintf(line_buf, LINE_BUF_SIZE, |
835 | " slab:%s buf:%s cache:%s dirty:%s write:%s", | 806 | " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", |
836 | Z[SLAB], Z[BUF], Z[CACHE], Z[DIRTY], Z[MWRITE]); | 807 | meminfo[MI_SLAB], |
808 | meminfo[MI_BUFFERS], | ||
809 | meminfo[MI_CACHED], | ||
810 | meminfo[MI_DIRTY], | ||
811 | meminfo[MI_WRITEBACK]); | ||
837 | printf("%.*s\n", scr_width, line_buf); | 812 | printf("%.*s\n", scr_width, line_buf); |
838 | 813 | ||
839 | snprintf(line_buf, LINE_BUF_SIZE, | 814 | snprintf(line_buf, LINE_BUF_SIZE, |
840 | "Swap total:%s free:%s", // TODO: % used? | 815 | "Swap total:%lu free:%lu", // TODO: % used? |
841 | Z[SWAPTOTAL], Z[SWAPFREE]); | 816 | meminfo[MI_SWAPTOTAL], |
817 | meminfo[MI_SWAPFREE]); | ||
842 | printf("%.*s\n", scr_width, line_buf); | 818 | printf("%.*s\n", scr_width, line_buf); |
843 | 819 | ||
844 | (*lines_rem_p) -= 3; | 820 | (*lines_rem_p) -= 3; |