1303 files changed, 32187 insertions, 57138 deletions
diff --git a/procps/top.c b/procps/top.c index 51f1c1a..71207ba 100644 --- a/procps/top.c +++ b/procps/top.c @@ -49,7 +49,6 @@ * cp stat meminfo loadavg proc * chroot . ./top -bn1 >top1.out */ - //config:config TOP //config: bool "top" //config: default y @@ -104,7 +103,12 @@ //config: help //config: Enable 's' in top (gives lots of memory info). +//applet:IF_TOP(APPLET(top, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_TOP) += top.o + #include "libbb.h" +#include "common_bufsiz.h" typedef struct top_status_t { @@ -183,11 +187,7 @@ struct globals { char line_buf[80]; }; //FIX_ALIASING; - large code growth enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) }; -#define G (*(struct globals*)&bb_common_bufsiz1) -struct BUG_bad_size { - char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; - char BUG_line_buf_too_small[LINE_BUF_SIZE > 80 ? 1 : -1]; -}; +#define G (*(struct globals*)bb_common_bufsiz1) #define top (G.top ) #define ntop (G.ntop ) #define sort_field (G.sort_field ) @@ -204,7 +204,11 @@ struct BUG_bad_size { #define num_cpus (G.num_cpus ) #define total_pcpu (G.total_pcpu ) #define line_buf (G.line_buf ) -#define INIT_G() do { } while (0) +#define INIT_G() do { \ + setup_common_bufsiz(); \ + BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \ + BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \ +} while (0) enum { OPT_d = (1 << 0), @@ -264,9 +268,9 @@ static int mult_lvl_cmp(void* a, void* b) static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif) { #if !ENABLE_FEATURE_TOP_SMP_CPU - static const char fmt[] = "cpu %llu %llu %llu %llu %llu %llu %llu %llu"; + static const char fmt[] ALIGN1 = "cpu %llu %llu %llu %llu %llu %llu %llu %llu"; #else - static const char fmt[] = "cp%*s %llu %llu %llu %llu %llu %llu %llu %llu"; + static const char fmt[] ALIGN1 = "cp%*s %llu %llu %llu %llu %llu %llu %llu %llu"; #endif int ret; @@ -499,85 +503,93 @@ 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; - - /* read memory info */ - fp = xfopen_for_read("meminfo"); +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 +}; - /* - * 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); +static void parse_meminfo(unsigned long meminfo[MI_MAX]) +{ + static const char fields[] ALIGN1 = + "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; - /* - * 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; + memset(meminfo, 0, sizeof(meminfo[0]) * MI_MAX); + 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); +} - 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) @@ -719,12 +731,6 @@ static void reset_term(void) { if (!OPT_BATCH_MODE) tcsetattr_stdin_TCSANOW(&initial_settings); - if (ENABLE_FEATURE_CLEAN_UP) { - clearmems(); -# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE - free(prev_hist); -# endif - } } static void sig_catcher(int sig) @@ -781,64 +787,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; @@ -855,10 +828,17 @@ static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width) #define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK" #define MIN_WIDTH sizeof(HDR_STR) const topmem_status_t *s = topmem + G_scroll_ofs; + char *cp, ch; display_topmem_header(scr_width, &lines_rem); + strcpy(line_buf, HDR_STR " COMMAND"); - line_buf[11 + sort_field * 6] = "^_"[inverted]; + /* Mark the ^FIELD^ we sort by */ + cp = &line_buf[5 + sort_field * 6]; + ch = "^_"[inverted]; + cp[6] = ch; + do *cp++ = ch; while (*cp == ' '); + printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf); lines_rem--; @@ -917,17 +897,12 @@ enum { #if ENABLE_FEATURE_USE_TERMIOS static unsigned handle_input(unsigned scan_mask, unsigned interval) { - struct pollfd pfd[1]; - if (option_mask32 & OPT_EOF) { /* EOF on stdin ("top </dev/null") */ sleep(interval); return scan_mask; } - pfd[0].fd = 0; - pfd[0].events = POLLIN; - while (1) { int32_t c; @@ -1067,7 +1042,9 @@ static unsigned handle_input(unsigned scan_mask, unsigned interval) //usage: "Provide a view of process activity in real time." //usage: "\n""Read the status of all processes from /proc each SECONDS" //usage: "\n""and display a screenful of them." -//usage: "\n""Keys:" +//usage: "\n" +//usage: IF_FEATURE_USE_TERMIOS( +//usage: "Keys:" //usage: "\n"" N/M" //usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/P") //usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/T") @@ -1087,6 +1064,7 @@ static unsigned handle_input(unsigned scan_mask, unsigned interval) //usage: "\n"" Q,^C: exit" //usage: "\n" //usage: "\n""Options:" +//usage: ) //usage: "\n"" -b Batch mode" //usage: "\n"" -n N Exit after N iterations" //usage: "\n"" -d N Delay between updates" @@ -1203,10 +1181,8 @@ int top_main(int argc UNUSED_PARAM, char **argv) ntop = 0; while ((p = procps_scan(p, scan_mask)) != NULL) { int n; -#if ENABLE_FEATURE_TOPMEM - if (scan_mask != TOPMEM_MASK) -#endif - { + + IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) { n = ntop; top = xrealloc_vector(top, 6, ntop++); top[n].pid = p->pid; @@ -1246,7 +1222,7 @@ int top_main(int argc UNUSED_PARAM, char **argv) break; } - if (scan_mask != TOPMEM_MASK) { + IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) { #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE if (!prev_hist_count) { do_stats(); @@ -1260,17 +1236,13 @@ int top_main(int argc UNUSED_PARAM, char **argv) #else qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0])); #endif + display_process_list(G.lines, col); } #if ENABLE_FEATURE_TOPMEM else { /* TOPMEM */ qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); - } -#endif - if (scan_mask != TOPMEM_MASK) - display_process_list(G.lines, col); -#if ENABLE_FEATURE_TOPMEM - else display_topmem_process_list(G.lines, col); + } #endif clearmems(); if (iterations >= 0 && !--iterations) @@ -1279,12 +1251,18 @@ int top_main(int argc UNUSED_PARAM, char **argv) sleep(interval); #else scan_mask = handle_input(scan_mask, interval); -#endif /* FEATURE_USE_TERMIOS */ +#endif } /* end of "while (not Q)" */ bb_putchar('\n'); #if ENABLE_FEATURE_USE_TERMIOS reset_term(); #endif + if (ENABLE_FEATURE_CLEAN_UP) { + clearmems(); +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + free(prev_hist); +#endif + } return EXIT_SUCCESS; } |