diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-09-25 14:48:06 +0400 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-09-25 14:48:06 +0400 |
commit | 17e7f04c8d144e0e0598464806dcb111ed5386d7 (patch) | |
tree | 002284cda156b2516c1d3a67352288741d7cd2e5 /procps/top.c | |
parent | e0bcba18eb9c185cefbef2ef05b846f735bd133a (diff) |
top: optional SMP support by Vineet Gupta (vineetg76 AT gmail.com)
Diffstat (limited to 'procps/top.c')
-rw-r--r-- | procps/top.c | 344 |
1 files changed, 260 insertions, 84 deletions
diff --git a/procps/top.c b/procps/top.c index 663eac674..c881ad419 100644 --- a/procps/top.c +++ b/procps/top.c @@ -16,6 +16,12 @@ * (C) Eero Tamminen <oak at welho dot com> * * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru> + * + * Sept 2008: Vineet Gupta <vineet.gupta@arc.com> + * Added Support for reporting SMP Information + * - CPU where Process was last seen running + * (to see effect of sched_setaffinity() etc) + * - CPU Time Split (idle/IO/wait etc) PER CPU */ /* Original code Copyrights */ @@ -41,6 +47,9 @@ typedef struct top_status_t { unsigned uid; char state[4]; char comm[COMM_LEN]; +#if ENABLE_FEATURE_TOP_SMP_PROCESS + int last_seen_on_cpu; +#endif } top_status_t; typedef struct jiffy_counts_t { @@ -69,6 +78,9 @@ struct globals { smallint sort_field; smallint inverted; #endif +#if ENABLE_FEATURE_TOP_SMP_CPU + smallint smp_cpu_info; /* one/many cpu info lines? */ +#endif #if ENABLE_FEATURE_USE_TERMIOS struct termios initial_settings; #endif @@ -83,6 +95,11 @@ struct globals { unsigned total_pcpu; /* unsigned long total_vsz; */ #endif +#if ENABLE_FEATURE_TOP_SMP_CPU + /* Per CPU samples: current and last */ + jiffy_counts_t *cpu_jif, *cpu_prev_jif; + int num_cpus; +#endif char line_buf[80]; }; @@ -98,12 +115,16 @@ enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) }; #define ntop (G.ntop ) #define sort_field (G.sort_field ) #define inverted (G.inverted ) +#define smp_cpu_info (G.smp_cpu_info ) #define initial_settings (G.initial_settings ) #define sort_function (G.sort_function ) #define prev_hist (G.prev_hist ) #define prev_hist_count (G.prev_hist_count ) #define jif (G.jif ) #define prev_jif (G.prev_jif ) +#define cpu_jif (G.cpu_jif ) +#define cpu_prev_jif (G.cpu_prev_jif ) +#define num_cpus (G.num_cpus ) #define total_pcpu (G.total_pcpu ) #define line_buf (G.line_buf ) @@ -161,24 +182,94 @@ static int mult_lvl_cmp(void* a, void* b) return 0; } +/* NOINLINE so that complier doesn't unfold the call + * causing multiple copies of the arithmatic instrns + */ +static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif) +{ +#if !ENABLE_FEATURE_TOP_SMP_CPU + static const char fmt[] = "cpu %lld %lld %lld %lld %lld %lld %lld %lld"; +#else + static const char fmt[] = "cp%*s %lld %lld %lld %lld %lld %lld %lld %lld"; +#endif + int ret; + + if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */) + return 0; + ret = sscanf(line_buf, fmt, + &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle, + &p_jif->iowait, &p_jif->irq, &p_jif->softirq, + &p_jif->steal); + if (ret > 4) { + p_jif->total = p_jif->usr + p_jif->nic + p_jif->sys + p_jif->idle + + p_jif->iowait + p_jif->irq + p_jif->softirq + p_jif->steal; + /* procps 2.x does not count iowait as busy time */ + p_jif->busy = p_jif->total - p_jif->idle - p_jif->iowait; + } + + return ret; +} static void get_jiffy_counts(void) { FILE* fp = xfopen_for_read("stat"); + +#if !ENABLE_FEATURE_TOP_SMP_CPU prev_jif = jif; - if (fscanf(fp, "cpu %lld %lld %lld %lld %lld %lld %lld %lld", - &jif.usr,&jif.nic,&jif.sys,&jif.idle, - &jif.iowait,&jif.irq,&jif.softirq,&jif.steal) < 4) { + if (read_cpu_jiffy(fp, &jif) < 4) bb_error_msg_and_die("can't read /proc/stat"); + fclose(fp); + return; +#else + if (!smp_cpu_info) { /* user wants to see cumulative cpu info */ + prev_jif = jif; + if (read_cpu_jiffy(fp, &jif) < 4) + bb_error_msg_and_die("can't read /proc/stat"); + fclose(fp); + return; + } + + /* Discard first "cpu ..." line */ + fgets(line_buf, LINE_BUF_SIZE, fp); + + if (!num_cpus) { + /* First time here. How many CPUs? + * There will be at least 1 /proc/stat line with cpu%d + */ + while (1) { + cpu_jif = xrealloc_vector(cpu_jif, 1, num_cpus); + if (read_cpu_jiffy(fp, &cpu_jif[num_cpus]) <= 4) + break; + num_cpus++; + } + if (num_cpus == 0) /* /proc/stat with only "cpu ..." line?! */ + smp_cpu_info = 0; + + /* TODO: need to cap num_cpus to a reasonable num + * on massively paralle machines so that we dont + * swamp the terminal with cpu "only" info + */ + cpu_prev_jif = xzalloc(sizeof(cpu_prev_jif[0]) * num_cpus); + + /* Otherwise the first per cpu display shows all 100% idles */ + usleep(50000); + } else { /* Non first time invocation */ + jiffy_counts_t *tmp; + int i; + + /* First switch the sample pointers: no need to copy */ + tmp = cpu_prev_jif; + cpu_prev_jif = cpu_jif; + cpu_jif = tmp; + + /* Get the new samples */ + for (i = 0; i < num_cpus; i++) + read_cpu_jiffy(fp, &cpu_jif[i]); } +#endif fclose(fp); - jif.total = jif.usr + jif.nic + jif.sys + jif.idle - + jif.iowait + jif.irq + jif.softirq + jif.steal; - /* procps 2.x does not count iowait as busy time */ - jif.busy = jif.total - jif.idle - jif.iowait; } - static void do_stats(void) { top_status_t *cur; @@ -189,7 +280,7 @@ static void do_stats(void) get_jiffy_counts(); total_pcpu = 0; /* total_vsz = 0; */ - new_hist = xmalloc(sizeof(struct save_hist)*ntop); + new_hist = xmalloc(sizeof(new_hist[0]) * ntop); /* * Make a pass through the data to get stats. */ @@ -230,6 +321,7 @@ static void do_stats(void) prev_hist = new_hist; prev_hist_count = ntop; } + #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ #if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS @@ -257,15 +349,92 @@ static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) } #endif -static unsigned long display_header(int scr_width) +#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS +static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) +{ + /* + * xxx% = (jif.xxx - prev_jif.xxx) / (jif.total - prev_jif.total) * 100% + */ + unsigned total_diff; + jiffy_counts_t *p_jif, *p_prev_jif; + int i; + +#if ENABLE_FEATURE_TOP_SMP_CPU + int n_cpu_lines; +#endif + + /* using (unsigned) casts to make operations cheaper */ +#define CALC_TOT_DIFF ((unsigned)(p_jif->total - p_prev_jif->total) ? : 1) + +#if ENABLE_FEATURE_TOP_DECIMALS +#define CALC_STAT(xxx) char xxx[8] +#define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(p_jif->xxx - p_prev_jif->xxx), total_diff) +#define FMT "%s" +#else +#define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(p_jif->xxx - p_prev_jif->xxx) / total_diff +#define SHOW_STAT(xxx) xxx +#define FMT "%4u%% " +#endif + +#if !ENABLE_FEATURE_TOP_SMP_CPU + { + i = 1; + p_jif = &jif; + p_prev_jif = &prev_jif; +#else + /* Loop thru CPU(s) */ + n_cpu_lines = smp_cpu_info ? num_cpus : 1; + if (n_cpu_lines > *lines_rem_p) + n_cpu_lines = *lines_rem_p; + + for (i = 0; i < n_cpu_lines; i++) { + /* set the real loop end */ + p_jif = &cpu_jif[i]; + p_prev_jif = &cpu_prev_jif[i]; +#endif + total_diff = CALC_TOT_DIFF; + + { /* Need block: CALC_STAT are declarations */ + CALC_STAT(usr); + CALC_STAT(sys); + CALC_STAT(nic); + CALC_STAT(idle); + CALC_STAT(iowait); + CALC_STAT(irq); + CALC_STAT(softirq); + /*CALC_STAT(steal);*/ + + snprintf(scrbuf, scr_width, + /* Barely fits in 79 chars when in "decimals" mode. */ +#if ENABLE_FEATURE_TOP_SMP_CPU + "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", + (smp_cpu_info ? utoa(i) : ""), +#else + "CPU:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", +#endif + SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle), + SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq) + /*, SHOW_STAT(steal) - what is this 'steal' thing? */ + /* I doubt anyone wants to know it */ + ); + puts(scrbuf); + } + } +#undef SHOW_STAT +#undef CALC_STAT +#undef FMT + *lines_rem_p -= i; +} +#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */ +#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; -#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS - unsigned total_diff; -#endif /* read memory info */ fp = xfopen_for_read("meminfo"); @@ -298,7 +467,6 @@ static unsigned long display_header(int scr_width) * sizes in kilobytes. This should be safe for both 2.4 and * 2.6. */ - fscanf(fp, "MemFree: %lu %s\n", &mfree, buf); /* @@ -323,47 +491,12 @@ static unsigned long display_header(int scr_width) used, mfree, shared, buffers, cached); /* clear screen & go to top */ printf(OPT_BATCH_MODE ? "%s\n" : "\e[H\e[J%s\n", scrbuf); + (*lines_rem_p)--; -#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS - /* - * xxx% = (jif.xxx - prev_jif.xxx) / (jif.total - prev_jif.total) * 100% + /* Display CPU time split as percentage of total time + * This displays either a cumulative line or one line per CPU */ - /* using (unsigned) casts to make operations cheaper */ - total_diff = ((unsigned)(jif.total - prev_jif.total) ? : 1); -#if ENABLE_FEATURE_TOP_DECIMALS -/* Generated code is approx +0.3k */ -#define CALC_STAT(xxx) char xxx[8] -#define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(jif.xxx - prev_jif.xxx), total_diff) -#define FMT "%s" -#else -#define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(jif.xxx - prev_jif.xxx) / total_diff -#define SHOW_STAT(xxx) xxx -#define FMT "%4u%% " -#endif - { /* need block: CALC_STAT are declarations */ - CALC_STAT(usr); - CALC_STAT(sys); - CALC_STAT(nic); - CALC_STAT(idle); - CALC_STAT(iowait); - CALC_STAT(irq); - CALC_STAT(softirq); - //CALC_STAT(steal); - - snprintf(scrbuf, scr_width, - /* Barely fits in 79 chars when in "decimals" mode. */ - "CPU:"FMT"usr"FMT"sys"FMT"nice"FMT"idle"FMT"io"FMT"irq"FMT"softirq", - SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle), - SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq) - //, SHOW_STAT(steal) - what is this 'steal' thing? - // I doubt anyone wants to know it - ); - } - puts(scrbuf); -#undef SHOW_STAT -#undef CALC_STAT -#undef FMT -#endif + display_cpus(scr_width, scrbuf, lines_rem_p); /* read load average as a string */ buf[0] = '\0'; @@ -371,35 +504,39 @@ static unsigned long display_header(int scr_width) buf[sizeof("N.NN N.NN N.NN")-1] = '\0'; snprintf(scrbuf, scr_width, "Load average: %s", buf); puts(scrbuf); + (*lines_rem_p)--; return total; } -static NOINLINE void display_process_list(int count, int scr_width) +static NOINLINE void display_process_list(int lines_rem, int scr_width) { enum { - BITS_PER_INT = sizeof(int)*8 + BITS_PER_INT = sizeof(int) * 8 }; - top_status_t *s = top; + top_status_t *s; char vsz_str_buf[8]; - unsigned long total_memory = display_header(scr_width); /* or use total_vsz? */ + unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */ /* xxx_shift and xxx_scale variables allow us to replace * expensive divides with multiply and shift */ unsigned pmem_shift, pmem_scale, pmem_half; #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE unsigned pcpu_shift, pcpu_scale, pcpu_half; unsigned busy_jifs; +#endif /* what info of the processes is shown */ printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, - " PID PPID USER STAT VSZ %MEM %CPU COMMAND"); -#else - - /* !CPU_USAGE_PERCENTAGE */ - printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, - " PID PPID USER STAT VSZ %MEM COMMAND"); + " PID PPID USER STAT VSZ %MEM" +#if ENABLE_FEATURE_TOP_SMP_PROCESS + " CPU" +#endif +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + " %CPU" #endif + " COMMAND"); + lines_rem--; #if ENABLE_FEATURE_TOP_DECIMALS #define UPSCALE 1000 @@ -453,9 +590,12 @@ static NOINLINE void display_process_list(int count, int scr_width) /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ #endif - scr_width += 2; /* account for leading '\n' and trailing NUL */ /* Ok, all preliminary data is ready, go through the list */ - while (count-- > 0) { + scr_width += 2; /* account for leading '\n' and trailing NUL */ + if (lines_rem > ntop) + lines_rem = ntop; + s = top; + while (--lines_rem >= 0) { unsigned col; CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift); #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE @@ -466,9 +606,12 @@ static NOINLINE void display_process_list(int count, int scr_width) sprintf(vsz_str_buf, "%6ldm", s->vsz/1024); else sprintf(vsz_str_buf, "%7ld", s->vsz); - // PID PPID USER STAT VSZ %MEM [%CPU] COMMAND + /* PID PPID USER STAT VSZ %MEM [%CPU] COMMAND */ col = snprintf(line_buf, scr_width, "\n" "%5u%6u %-8.8s %s%s" FMT +#if ENABLE_FEATURE_TOP_SMP_PROCESS + " %3d" +#endif #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE FMT #endif @@ -476,6 +619,9 @@ static NOINLINE void display_process_list(int count, int scr_width) s->pid, s->ppid, get_cached_username(s->uid), s->state, vsz_str_buf, SHOW_STAT(pmem) +#if ENABLE_FEATURE_TOP_SMP_PROCESS + , s->last_seen_on_cpu +#endif #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE , SHOW_STAT(pcpu) #endif @@ -580,7 +726,7 @@ static char *grab_number(char *str, const char *match, unsigned sz) } /* display header info (meminfo / loadavg) */ -static void display_topmem_header(int scr_width) +static void display_topmem_header(int scr_width, int *lines_rem_p) { char linebuf[128]; unsigned i; @@ -655,6 +801,8 @@ static void display_topmem_header(int scr_width) "Swap %stotal %sfree", // TODO: % used? S(swaptotal), S(swapfree)); printf("%.*s\n", scr_width, linebuf); + + (*lines_rem_p) -= 3; #undef S for (i = 0; i < ARRAY_SIZE(str); i++) @@ -680,19 +828,22 @@ static void ulltoa6_and_space(unsigned long long ul, char buf[6]) buf[5] = ' '; } -static NOINLINE void display_topmem_process_list(int count, int scr_width) +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; - display_topmem_header(scr_width); + display_topmem_header(scr_width, &lines_rem); strcpy(line_buf, HDR_STR " COMMAND"); line_buf[5 + sort_field * 6] = '*'; printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf); + lines_rem--; - while (--count >= 0) { - // PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND + if (lines_rem > ntop) + lines_rem = ntop; + while (--lines_rem >= 0) { + /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */ ulltoa6_and_space(s->pid , &line_buf[0*6]); ulltoa6_and_space(s->vsz , &line_buf[1*6]); ulltoa6_and_space(s->vszrw , &line_buf[2*6]); @@ -714,7 +865,7 @@ static NOINLINE void display_topmem_process_list(int count, int scr_width) #undef MIN_WIDTH } #else -void display_topmem_process_list(int count, int scr_width); +void display_topmem_process_list(int lines_rem, int scr_width); int topmem_sort(char *a, char *b); #endif /* TOPMEM */ @@ -731,6 +882,9 @@ enum { | PSSCAN_UTIME | PSSCAN_STATE | PSSCAN_COMM +#if ENABLE_FEATURE_TOP_SMP_PROCESS + | PSSCAN_CPU +#endif | PSSCAN_UIDGID, TOPMEM_MASK = 0 | PSSCAN_PID @@ -741,9 +895,9 @@ enum { int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int top_main(int argc UNUSED_PARAM, char **argv) { - int count; int iterations; unsigned lines, col; + int lines_rem; unsigned interval; char *sinterval; SKIP_FEATURE_TOPMEM(const) unsigned scan_mask = TOP_MASK; @@ -760,12 +914,18 @@ int top_main(int argc UNUSED_PARAM, char **argv) interval = 5; /* default update interval is 5 seconds */ iterations = 0; /* infinite */ +#if ENABLE_FEATURE_TOP_SMP_CPU + /*num_cpus = 0;*/ + /*smp_cpu_info = 0;*/ /* to start with show aggregate */ + cpu_jif = &jif; + cpu_prev_jif = &prev_jif; +#endif /* all args are options; -n NUM */ opt_complementary = "-:n+"; if (getopt32(argv, "d:n:b", &sinterval, &iterations) & OPT_d) { /* Need to limit it to not overflow poll timeout */ - interval = xatou16(sinterval); // -d + interval = xatou16(sinterval); /* -d */ } /* change to /proc */ @@ -803,10 +963,6 @@ int top_main(int argc UNUSED_PARAM, char **argv) #endif /* FEATURE_USE_TERMIOS */ if (col > LINE_BUF_SIZE-2) /* +2 bytes for '\n', NUL, */ col = LINE_BUF_SIZE-2; - if (!ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && scan_mask == TOP_MASK) - lines -= 3; - else - lines -= 4; /* read process IDs & status for all the processes */ while ((p = procps_scan(p, scan_mask)) != NULL) { @@ -823,6 +979,9 @@ int top_main(int argc UNUSED_PARAM, char **argv) top[n].uid = p->uid; strcpy(top[n].state, p->state); strcpy(top[n].comm, p->comm); +#if ENABLE_FEATURE_TOP_SMP_PROCESS + top[n].last_seen_on_cpu = p->last_seen_on_cpu; +#endif } else { /* TOPMEM */ #if ENABLE_FEATURE_TOPMEM if (!(p->mapped_ro | p->mapped_rw)) @@ -856,7 +1015,7 @@ int top_main(int argc UNUSED_PARAM, char **argv) continue; } do_stats(); -/* TODO: we don't need to sort all 10000 processes, we need to find top 24! */ + /* TODO: we don't need to sort all 10000 processes, we need to find top 24! */ qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp); #else qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0])); @@ -867,15 +1026,15 @@ int top_main(int argc UNUSED_PARAM, char **argv) qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); } #endif - count = lines; - if (OPT_BATCH_MODE || count > ntop) { - count = ntop; + lines_rem = lines; + if (OPT_BATCH_MODE) { + lines_rem = INT_MAX; } if (scan_mask == TOP_MASK) - display_process_list(count, col); + display_process_list(lines_rem, col); #if ENABLE_FEATURE_TOPMEM else - display_topmem_process_list(count, col); + display_topmem_process_list(lines_rem, col); #endif clearmems(); if (iterations >= 0 && !--iterations) @@ -932,6 +1091,23 @@ int top_main(int argc UNUSED_PARAM, char **argv) if (c == 'r') inverted ^= 1; #endif +#if ENABLE_FEATURE_TOP_SMP_CPU + if (c == 'c') { /* procps-2.0.18 uses 'C' */ + /* User wants to toggle per cpu <> aggregate */ + if (smp_cpu_info) { + free(cpu_prev_jif); + free(cpu_jif); + cpu_jif = &jif; + cpu_prev_jif = &prev_jif; + } else { + /* Prepare for xrealloc() */ + cpu_jif = cpu_prev_jif = NULL; + } + num_cpus = 0; + smp_cpu_info = !smp_cpu_info; + get_jiffy_counts(); + } +#endif #endif } #endif /* FEATURE_USE_TERMIOS */ |