top: fix and merge code to parse /proc/meminfo
No binary (build failed)
—
(log yok)
| @@ -499,85 +499,94 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) | |
| # define display_cpus(scr_width, scrbuf, lines_rem) ((void)0) | |
| #endif | |
| - | static unsigned long display_header(int scr_width, int *lines_rem_p) |
| - | { |
| - | FILE *fp; |
| - | char buf[80]; |
| - | char scrbuf[80]; |
| - | unsigned long total, used, mfree, shared, buffers, cached; |
| + | enum { |
| + | MI_MEMTOTAL, |
| + | MI_MEMFREE, |
| + | MI_MEMSHARED, |
| + | MI_SHMEM, |
| + | MI_BUFFERS, |
| + | MI_CACHED, |
| + | MI_SWAPTOTAL, |
| + | MI_SWAPFREE, |
| + | MI_DIRTY, |
| + | MI_WRITEBACK, |
| + | MI_ANONPAGES, |
| + | MI_MAPPED, |
| + | MI_SLAB, |
| + | MI_MAX |
| + | }; |
| - | /* read memory info */ |
| - | fp = xfopen_for_read("meminfo"); |
| + | static void parse_meminfo(unsigned long meminfo[MI_MAX]) |
| + | { |
| + | static const char fields[] = |
| + | "MemTotal\0" |
| + | "MemFree\0" |
| + | "MemShared\0" |
| + | "Shmem\0" |
| + | "Buffers\0" |
| + | "Cached\0" |
| + | "SwapTotal\0" |
| + | "SwapFree\0" |
| + | "Dirty\0" |
| + | "Writeback\0" |
| + | "AnonPages\0" |
| + | "Mapped\0" |
| + | "Slab\0"; |
| + | char buf[60]; /* actual lines we expect are ~30 chars or less */ |
| + | FILE *f; |
| + | int i; |
| - | /* |
| - | * Old kernels (such as 2.4.x) had a nice summary of memory info that |
| - | * we could parse, however this is gone entirely in 2.6. Try parsing |
| - | * the old way first, and if that fails, parse each field manually. |
| - | * |
| - | * First, we read in the first line. Old kernels will have bogus |
| - | * strings we don't care about, whereas new kernels will start right |
| - | * out with MemTotal: |
| - | * -- PFM. |
| - | */ |
| - | if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) { |
| - | fgets(buf, sizeof(buf), fp); /* skip first line */ |
| - | |
| - | fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu", |
| - | &total, &used, &mfree, &shared, &buffers, &cached); |
| - | /* convert to kilobytes */ |
| - | used /= 1024; |
| - | mfree /= 1024; |
| - | shared /= 1024; |
| - | buffers /= 1024; |
| - | cached /= 1024; |
| - | total /= 1024; |
| - | } else { |
| - | /* |
| - | * Revert to manual parsing, which incidentally already has the |
| - | * sizes in kilobytes. This should be safe for both 2.4 and |
| - | * 2.6. |
| - | */ |
| - | fscanf(fp, "MemFree: %lu %s\n", &mfree, buf); |
| + | memset(meminfo, 0, sizeof(meminfo)); |
| + | f = xfopen_for_read("meminfo"); |
| + | while (fgets(buf, sizeof(buf), f) != NULL) { |
| + | char *c = strchr(buf, ':'); |
| + | if (!c) |
| + | continue; |
| + | *c = '\0'; |
| + | i = index_in_strings(fields, buf); |
| + | if (i >= 0) |
| + | meminfo[i] = strtoul(c+1, NULL, 10); |
| + | } |
| + | fclose(f); |
| + | } |
| - | /* |
| - | * MemShared: is no longer present in 2.6. Report this as 0, |
| - | * to maintain consistent behavior with normal procps. |
| - | */ |
| - | if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2) |
| - | shared = 0; |
| - | fscanf(fp, "Buffers: %lu %s\n", &buffers, buf); |
| - | fscanf(fp, "Cached: %lu %s\n", &cached, buf); |
| + | static unsigned long display_header(int scr_width, int *lines_rem_p) |
| + | { |
| + | char scrbuf[100]; /* [80] was a bit too low on 8Gb ram box */ |
| + | char *buf; |
| + | unsigned long meminfo[MI_MAX]; |
| - | used = total - mfree; |
| - | } |
| - | fclose(fp); |
| + | parse_meminfo(meminfo); |
| - | /* output memory info */ |
| + | /* Output memory info */ |
| if (scr_width > (int)sizeof(scrbuf)) | |
| scr_width = sizeof(scrbuf); | |
| snprintf(scrbuf, scr_width, | |
| "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", | |
| - | used, mfree, shared, buffers, cached); |
| - | /* go to top & clear to the end of screen */ |
| + | meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE], |
| + | meminfo[MI_MEMFREE], |
| + | meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM], |
| + | meminfo[MI_BUFFERS], |
| + | meminfo[MI_CACHED]); |
| + | /* Go to top & clear to the end of screen */ |
| printf(OPT_BATCH_MODE ? "%s\n" : "\033[H\033[J%s\n", scrbuf); | |
| (*lines_rem_p)--; | |
| - | /* Display CPU time split as percentage of total time |
| - | * This displays either a cumulative line or one line per CPU |
| + | /* Display CPU time split as percentage of total time. |
| + | * This displays either a cumulative line or one line per CPU. |
| */ | |
| display_cpus(scr_width, scrbuf, lines_rem_p); | |
| - | /* read load average as a string */ |
| - | buf[0] = '\0'; |
| - | open_read_close("loadavg", buf, sizeof(buf) - 1); |
| - | buf[sizeof(buf) - 1] = '\n'; |
| - | *strchr(buf, '\n') = '\0'; |
| - | snprintf(scrbuf, scr_width, "Load average: %s", buf); |
| + | /* Read load average as a string */ |
| + | buf = stpcpy(scrbuf, "Load average: "); |
| + | open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: ")); |
| + | scrbuf[scr_width - 1] = '\0'; |
| + | strchrnul(buf, '\n')[0] = '\0'; |
| puts(scrbuf); | |
| (*lines_rem_p)--; | |
| - | return total; |
| + | return meminfo[MI_MEMTOTAL]; |
| } | |
| static NOINLINE void display_process_list(int lines_rem, int scr_width) | |
| @@ -781,64 +790,31 @@ static int topmem_sort(char *a, char *b) | |
| /* display header info (meminfo / loadavg) */ | |
| static void display_topmem_header(int scr_width, int *lines_rem_p) | |
| { | |
| - | enum { |
| - | TOTAL = 0, MFREE, BUF, CACHE, |
| - | SWAPTOTAL, SWAPFREE, DIRTY, |
| - | MWRITE, ANON, MAP, SLAB, |
| - | NUM_FIELDS |
| - | }; |
| - | static const char match[NUM_FIELDS][12] = { |
| - | "\x09" "MemTotal:", // TOTAL |
| - | "\x08" "MemFree:", // MFREE |
| - | "\x08" "Buffers:", // BUF |
| - | "\x07" "Cached:", // CACHE |
| - | "\x0a" "SwapTotal:", // SWAPTOTAL |
| - | "\x09" "SwapFree:", // SWAPFREE |
| - | "\x06" "Dirty:", // DIRTY |
| - | "\x0a" "Writeback:", // MWRITE |
| - | "\x0a" "AnonPages:", // ANON |
| - | "\x07" "Mapped:", // MAP |
| - | "\x05" "Slab:", // SLAB |
| - | }; |
| - | char meminfo_buf[4 * 1024]; |
| - | const char *Z[NUM_FIELDS]; |
| - | unsigned i; |
| - | int sz; |
| - | |
| - | for (i = 0; i < NUM_FIELDS; i++) |
| - | Z[i] = "?"; |
| - | |
| - | /* read memory info */ |
| - | sz = open_read_close("meminfo", meminfo_buf, sizeof(meminfo_buf) - 1); |
| - | if (sz >= 0) { |
| - | char *p = meminfo_buf; |
| - | meminfo_buf[sz] = '\0'; |
| - | /* Note that fields always appear in the match[] order */ |
| - | for (i = 0; i < NUM_FIELDS; i++) { |
| - | char *found = strstr(p, match[i] + 1); |
| - | if (found) { |
| - | /* Cut "NNNN" out of " NNNN kb" */ |
| - | char *s = skip_whitespace(found + match[i][0]); |
| - | p = skip_non_whitespace(s); |
| - | *p++ = '\0'; |
| - | Z[i] = s; |
| - | } |
| - | } |
| - | } |
| + | unsigned long meminfo[MI_MAX]; |
| + | |
| + | parse_meminfo(meminfo); |
| snprintf(line_buf, LINE_BUF_SIZE, | |
| - | "Mem total:%s anon:%s map:%s free:%s", |
| - | Z[TOTAL], Z[ANON], Z[MAP], Z[MFREE]); |
| + | "Mem total:%lu anon:%lu map:%lu free:%lu", |
| + | meminfo[MI_MEMTOTAL], |
| + | meminfo[MI_ANONPAGES], |
| + | meminfo[MI_MAPPED], |
| + | meminfo[MI_MEMFREE]); |
| printf(OPT_BATCH_MODE ? "%.*s\n" : "\033[H\033[J%.*s\n", scr_width, line_buf); | |
| snprintf(line_buf, LINE_BUF_SIZE, | |
| - | " slab:%s buf:%s cache:%s dirty:%s write:%s", |
| - | Z[SLAB], Z[BUF], Z[CACHE], Z[DIRTY], Z[MWRITE]); |
| + | " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", |
| + | meminfo[MI_SLAB], |
| + | meminfo[MI_BUFFERS], |
| + | meminfo[MI_CACHED], |
| + | meminfo[MI_DIRTY], |
| + | meminfo[MI_WRITEBACK]); |
| printf("%.*s\n", scr_width, line_buf); | |
| snprintf(line_buf, LINE_BUF_SIZE, | |
| - | "Swap total:%s free:%s", // TODO: % used? |
| - | Z[SWAPTOTAL], Z[SWAPFREE]); |
| + | "Swap total:%lu free:%lu", // TODO: % used? |
| + | meminfo[MI_SWAPTOTAL], |
| + | meminfo[MI_SWAPFREE]); |
| printf("%.*s\n", scr_width, line_buf); | |
| (*lines_rem_p) -= 3; | |