aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2014-07-21 14:14:24 +0300
committerDenys Vlasenko <vda.linux@googlemail.com>2014-07-27 21:49:27 +0200
commit7df1f1dda1f997c44800d16a9a12cf6cae2ed7e7 (patch)
tree6e5f6e27360e50a574c4e03c624ab0b07576f57c
parent2a563ea49a16589f47ed6afe7b22eebef4e3a6d1 (diff)
downloadbusybox-w32-7df1f1dda1f997c44800d16a9a12cf6cae2ed7e7.tar.gz
busybox-w32-7df1f1dda1f997c44800d16a9a12cf6cae2ed7e7.tar.bz2
busybox-w32-7df1f1dda1f997c44800d16a9a12cf6cae2ed7e7.zip
top: fix and merge code to parse /proc/meminfo
display_header() code to parse meminfo as is was buggy: - uninitialized variables were used if meminfo was not as expected - meminfo parsing failed on new kernels (3.14+) as new field 'MemAvailable' was introduced between MemFree and Buffers - shared memory was handled only for ancient kernels (2.4.x and earlier) as result Buffers and shared memory fields were shown with bogus values on current kernels. The new code does not try to parse the old style summary header, as the separated fields are always present (it saves code size). Additionally, both Shmem (2.6+) and MemShared (2.4 and earlier) fields are now parsed and summed for shared memory usage; as only one of them exists depending on kernel version. display_topmem_header() parses also meminfo so this makes it use the same code for code shrink. function old new delta display_header - 681 +681 display_topmem_process_list 465 684 +219 parse_meminfo - 189 +189 static.fields - 106 +106 static.match 132 - -132 .rodata 120254 120117 -137 display_topmem_header 513 - -513 display_process_list 1705 667 -1038 ------------------------------------------------------------------------------ (add/remove: 3/2 grow/shrink: 1/2 up/down: 1195/-1820) Total: -625 bytes Signed-off-by: Timo Teräs <timo.teras@iki.fi> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--procps/top.c196
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
502static unsigned long display_header(int scr_width, int *lines_rem_p) 502enum {
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 */ 519static 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); 554static 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
583static NOINLINE void display_process_list(int lines_rem, int scr_width) 592static 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) */
782static void display_topmem_header(int scr_width, int *lines_rem_p) 791static 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;