aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-09-25 10:48:06 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-09-25 10:48:06 +0000
commit17e7f04c8d144e0e0598464806dcb111ed5386d7 (patch)
tree002284cda156b2516c1d3a67352288741d7cd2e5
parente0bcba18eb9c185cefbef2ef05b846f735bd133a (diff)
downloadbusybox-w32-17e7f04c8d144e0e0598464806dcb111ed5386d7.tar.gz
busybox-w32-17e7f04c8d144e0e0598464806dcb111ed5386d7.tar.bz2
busybox-w32-17e7f04c8d144e0e0598464806dcb111ed5386d7.zip
top: optional SMP support by Vineet Gupta (vineetg76 AT gmail.com)
-rw-r--r--include/libbb.h15
-rw-r--r--libbb/procps.c33
-rw-r--r--procps/Config.in17
-rw-r--r--procps/top.c344
4 files changed, 312 insertions, 97 deletions
diff --git a/include/libbb.h b/include/libbb.h
index 3b4715e33..951ff2f1a 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1225,6 +1225,9 @@ typedef struct procps_status_t {
1225 * by link target or interpreter name) */ 1225 * by link target or interpreter name) */
1226 char comm[COMM_LEN]; 1226 char comm[COMM_LEN];
1227 /* user/group? - use passwd/group parsing functions */ 1227 /* user/group? - use passwd/group parsing functions */
1228#if ENABLE_FEATURE_TOP_SMP_PROCESS
1229 int last_seen_on_cpu;
1230#endif
1228} procps_status_t; 1231} procps_status_t;
1229enum { 1232enum {
1230 PSSCAN_PID = 1 << 0, 1233 PSSCAN_PID = 1 << 0,
@@ -1246,12 +1249,16 @@ enum {
1246 PSSCAN_ARGVN = (1 << 16) * (ENABLE_PGREP || ENABLE_PKILL || ENABLE_PIDOF), 1249 PSSCAN_ARGVN = (1 << 16) * (ENABLE_PGREP || ENABLE_PKILL || ENABLE_PIDOF),
1247 USE_SELINUX(PSSCAN_CONTEXT = 1 << 17,) 1250 USE_SELINUX(PSSCAN_CONTEXT = 1 << 17,)
1248 PSSCAN_START_TIME = 1 << 18, 1251 PSSCAN_START_TIME = 1 << 18,
1252 PSSCAN_CPU = 1 << 19,
1249 /* These are all retrieved from proc/NN/stat in one go: */ 1253 /* These are all retrieved from proc/NN/stat in one go: */
1250 PSSCAN_STAT = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID 1254 PSSCAN_STAT = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID
1251 | PSSCAN_COMM | PSSCAN_STATE 1255 /**/ | PSSCAN_COMM | PSSCAN_STATE
1252 | PSSCAN_VSZ | PSSCAN_RSS 1256 /**/ | PSSCAN_VSZ | PSSCAN_RSS
1253 | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_START_TIME 1257 /**/ | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_START_TIME
1254 | PSSCAN_TTY, 1258 /**/ | PSSCAN_TTY
1259#if ENABLE_FEATURE_TOP_SMP_PROCESS
1260 /**/ | PSSCAN_CPU
1261#endif
1255}; 1262};
1256//procps_status_t* alloc_procps_scan(void) FAST_FUNC; 1263//procps_status_t* alloc_procps_scan(void) FAST_FUNC;
1257void free_procps_scan(procps_status_t* sp) FAST_FUNC; 1264void free_procps_scan(procps_status_t* sp) FAST_FUNC;
diff --git a/libbb/procps.c b/libbb/procps.c
index fd19621db..4d9a95b4f 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -219,7 +219,6 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
219#if !ENABLE_FEATURE_FAST_TOP 219#if !ENABLE_FEATURE_FAST_TOP
220 unsigned long vsz, rss; 220 unsigned long vsz, rss;
221#endif 221#endif
222
223 /* see proc(5) for some details on this */ 222 /* see proc(5) for some details on this */
224 strcpy(filename_tail, "/stat"); 223 strcpy(filename_tail, "/stat");
225 n = read_to_buf(filename, buf); 224 n = read_to_buf(filename, buf);
@@ -247,9 +246,12 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
247 "%lu " /* start_time */ 246 "%lu " /* start_time */
248 "%lu " /* vsize */ 247 "%lu " /* vsize */
249 "%lu " /* rss */ 248 "%lu " /* rss */
250 /* "%lu %lu %lu %lu %lu %lu " rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ 249#if ENABLE_FEATURE_TOP_SMP_PROCESS
251 /* "%u %u %u %u " signal, blocked, sigignore, sigcatch */ 250 "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
252 /* "%lu %lu %lu" wchan, nswap, cnswap */ 251 "%*s %*s %*s %*s " /*signal, blocked, sigignore, sigcatch */
252 "%*s %*s %*s %*s " /*wchan, nswap, cnswap, exit_signal */
253 "%d" /*cpu last seen on*/
254#endif
253 , 255 ,
254 sp->state, &sp->ppid, 256 sp->state, &sp->ppid,
255 &sp->pgid, &sp->sid, &tty, 257 &sp->pgid, &sp->sid, &tty,
@@ -257,9 +259,19 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
257 &tasknice, 259 &tasknice,
258 &sp->start_time, 260 &sp->start_time,
259 &vsz, 261 &vsz,
260 &rss); 262 &rss
261 if (n != 11) 263#if ENABLE_FEATURE_TOP_SMP_PROCESS
264 , &sp->last_seen_on_cpu
265#endif
266 );
267
268 if (n < 11)
262 break; 269 break;
270#if ENABLE_FEATURE_TOP_SMP_PROCESS
271 if (n < 11+15)
272 sp->last_seen_on_cpu = 0;
273#endif
274
263 /* vsz is in bytes and we want kb */ 275 /* vsz is in bytes and we want kb */
264 sp->vsz = vsz >> 10; 276 sp->vsz = vsz >> 10;
265 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ 277 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */
@@ -288,7 +300,15 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
288 sp->vsz = fast_strtoul_10(&cp) >> 10; 300 sp->vsz = fast_strtoul_10(&cp) >> 10;
289 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ 301 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */
290 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; 302 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb;
303#if ENABLE_FEATURE_TOP_SMP_PROCESS
304 /* (6): rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
305 /* (4): signal, blocked, sigignore, sigcatch */
306 /* (4): wchan, nswap, cnswap, exit_signal */
307 cp = skip_fields(cp, 14);
308//FIXME: is it safe to assume this field exists?
309 sp->last_seen_on_cpu = fast_strtoul_10(&cp);
291#endif 310#endif
311#endif /* end of !ENABLE_FEATURE_TOP_SMP_PROCESS */
292 312
293 if (sp->vsz == 0 && sp->state[0] != 'Z') 313 if (sp->vsz == 0 && sp->state[0] != 'Z')
294 sp->state[1] = 'W'; 314 sp->state[1] = 'W';
@@ -300,7 +320,6 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
300 sp->state[2] = 'N'; 320 sp->state[2] = 'N';
301 else 321 else
302 sp->state[2] = ' '; 322 sp->state[2] = ' ';
303
304 } 323 }
305 324
306#if ENABLE_FEATURE_TOPMEM 325#if ENABLE_FEATURE_TOPMEM
diff --git a/procps/Config.in b/procps/Config.in
index 642ebd015..548463c67 100644
--- a/procps/Config.in
+++ b/procps/Config.in
@@ -148,6 +148,13 @@ config FEATURE_TOP_CPU_GLOBAL_PERCENTS
148 help 148 help
149 Makes top display "CPU: NN% usr NN% sys..." line. 149 Makes top display "CPU: NN% usr NN% sys..." line.
150 150
151config FEATURE_TOP_SMP_CPU
152 bool "SMP CPU usage display ('c' key) (adds 0.5kb)"
153 default n
154 depends on FEATURE_TOP_CPU_GLOBAL_PERCENTS
155 help
156 Allos 'c' key to switch between individual/cumulative CPU stats
157
151config FEATURE_TOP_DECIMALS 158config FEATURE_TOP_DECIMALS
152 bool "Show 1/10th of a percent in CPU/mem statistics (adds 0.3k bytes)" 159 bool "Show 1/10th of a percent in CPU/mem statistics (adds 0.3k bytes)"
153 default n 160 default n
@@ -155,8 +162,15 @@ config FEATURE_TOP_DECIMALS
155 help 162 help
156 Show 1/10th of a percent in CPU/mem statistics. 163 Show 1/10th of a percent in CPU/mem statistics.
157 164
165config FEATURE_TOP_SMP_PROCESS
166 bool "Show CPU process runs on (adds <0.1k bytes)"
167 default n
168 depends on TOP
169 help
170 Show CPU where process was last found running on
171
158config FEATURE_TOPMEM 172config FEATURE_TOPMEM
159 bool "topmem" 173 bool "Topmem command ('s' key)"
160 default n 174 default n
161 depends on TOP 175 depends on TOP
162 help 176 help
@@ -180,4 +194,3 @@ config WATCH
180 194
181 195
182endmenu 196endmenu
183
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 @@
16 * (C) Eero Tamminen <oak at welho dot com> 16 * (C) Eero Tamminen <oak at welho dot com>
17 * 17 *
18 * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru> 18 * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
19 *
20 * Sept 2008: Vineet Gupta <vineet.gupta@arc.com>
21 * Added Support for reporting SMP Information
22 * - CPU where Process was last seen running
23 * (to see effect of sched_setaffinity() etc)
24 * - CPU Time Split (idle/IO/wait etc) PER CPU
19 */ 25 */
20 26
21/* Original code Copyrights */ 27/* Original code Copyrights */
@@ -41,6 +47,9 @@ typedef struct top_status_t {
41 unsigned uid; 47 unsigned uid;
42 char state[4]; 48 char state[4];
43 char comm[COMM_LEN]; 49 char comm[COMM_LEN];
50#if ENABLE_FEATURE_TOP_SMP_PROCESS
51 int last_seen_on_cpu;
52#endif
44} top_status_t; 53} top_status_t;
45 54
46typedef struct jiffy_counts_t { 55typedef struct jiffy_counts_t {
@@ -69,6 +78,9 @@ struct globals {
69 smallint sort_field; 78 smallint sort_field;
70 smallint inverted; 79 smallint inverted;
71#endif 80#endif
81#if ENABLE_FEATURE_TOP_SMP_CPU
82 smallint smp_cpu_info; /* one/many cpu info lines? */
83#endif
72#if ENABLE_FEATURE_USE_TERMIOS 84#if ENABLE_FEATURE_USE_TERMIOS
73 struct termios initial_settings; 85 struct termios initial_settings;
74#endif 86#endif
@@ -83,6 +95,11 @@ struct globals {
83 unsigned total_pcpu; 95 unsigned total_pcpu;
84 /* unsigned long total_vsz; */ 96 /* unsigned long total_vsz; */
85#endif 97#endif
98#if ENABLE_FEATURE_TOP_SMP_CPU
99 /* Per CPU samples: current and last */
100 jiffy_counts_t *cpu_jif, *cpu_prev_jif;
101 int num_cpus;
102#endif
86 char line_buf[80]; 103 char line_buf[80];
87}; 104};
88 105
@@ -98,12 +115,16 @@ enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) };
98#define ntop (G.ntop ) 115#define ntop (G.ntop )
99#define sort_field (G.sort_field ) 116#define sort_field (G.sort_field )
100#define inverted (G.inverted ) 117#define inverted (G.inverted )
118#define smp_cpu_info (G.smp_cpu_info )
101#define initial_settings (G.initial_settings ) 119#define initial_settings (G.initial_settings )
102#define sort_function (G.sort_function ) 120#define sort_function (G.sort_function )
103#define prev_hist (G.prev_hist ) 121#define prev_hist (G.prev_hist )
104#define prev_hist_count (G.prev_hist_count ) 122#define prev_hist_count (G.prev_hist_count )
105#define jif (G.jif ) 123#define jif (G.jif )
106#define prev_jif (G.prev_jif ) 124#define prev_jif (G.prev_jif )
125#define cpu_jif (G.cpu_jif )
126#define cpu_prev_jif (G.cpu_prev_jif )
127#define num_cpus (G.num_cpus )
107#define total_pcpu (G.total_pcpu ) 128#define total_pcpu (G.total_pcpu )
108#define line_buf (G.line_buf ) 129#define line_buf (G.line_buf )
109 130
@@ -161,24 +182,94 @@ static int mult_lvl_cmp(void* a, void* b)
161 return 0; 182 return 0;
162} 183}
163 184
185/* NOINLINE so that complier doesn't unfold the call
186 * causing multiple copies of the arithmatic instrns
187 */
188static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
189{
190#if !ENABLE_FEATURE_TOP_SMP_CPU
191 static const char fmt[] = "cpu %lld %lld %lld %lld %lld %lld %lld %lld";
192#else
193 static const char fmt[] = "cp%*s %lld %lld %lld %lld %lld %lld %lld %lld";
194#endif
195 int ret;
196
197 if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */)
198 return 0;
199 ret = sscanf(line_buf, fmt,
200 &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle,
201 &p_jif->iowait, &p_jif->irq, &p_jif->softirq,
202 &p_jif->steal);
203 if (ret > 4) {
204 p_jif->total = p_jif->usr + p_jif->nic + p_jif->sys + p_jif->idle
205 + p_jif->iowait + p_jif->irq + p_jif->softirq + p_jif->steal;
206 /* procps 2.x does not count iowait as busy time */
207 p_jif->busy = p_jif->total - p_jif->idle - p_jif->iowait;
208 }
209
210 return ret;
211}
164 212
165static void get_jiffy_counts(void) 213static void get_jiffy_counts(void)
166{ 214{
167 FILE* fp = xfopen_for_read("stat"); 215 FILE* fp = xfopen_for_read("stat");
216
217#if !ENABLE_FEATURE_TOP_SMP_CPU
168 prev_jif = jif; 218 prev_jif = jif;
169 if (fscanf(fp, "cpu %lld %lld %lld %lld %lld %lld %lld %lld", 219 if (read_cpu_jiffy(fp, &jif) < 4)
170 &jif.usr,&jif.nic,&jif.sys,&jif.idle,
171 &jif.iowait,&jif.irq,&jif.softirq,&jif.steal) < 4) {
172 bb_error_msg_and_die("can't read /proc/stat"); 220 bb_error_msg_and_die("can't read /proc/stat");
221 fclose(fp);
222 return;
223#else
224 if (!smp_cpu_info) { /* user wants to see cumulative cpu info */
225 prev_jif = jif;
226 if (read_cpu_jiffy(fp, &jif) < 4)
227 bb_error_msg_and_die("can't read /proc/stat");
228 fclose(fp);
229 return;
230 }
231
232 /* Discard first "cpu ..." line */
233 fgets(line_buf, LINE_BUF_SIZE, fp);
234
235 if (!num_cpus) {
236 /* First time here. How many CPUs?
237 * There will be at least 1 /proc/stat line with cpu%d
238 */
239 while (1) {
240 cpu_jif = xrealloc_vector(cpu_jif, 1, num_cpus);
241 if (read_cpu_jiffy(fp, &cpu_jif[num_cpus]) <= 4)
242 break;
243 num_cpus++;
244 }
245 if (num_cpus == 0) /* /proc/stat with only "cpu ..." line?! */
246 smp_cpu_info = 0;
247
248 /* TODO: need to cap num_cpus to a reasonable num
249 * on massively paralle machines so that we dont
250 * swamp the terminal with cpu "only" info
251 */
252 cpu_prev_jif = xzalloc(sizeof(cpu_prev_jif[0]) * num_cpus);
253
254 /* Otherwise the first per cpu display shows all 100% idles */
255 usleep(50000);
256 } else { /* Non first time invocation */
257 jiffy_counts_t *tmp;
258 int i;
259
260 /* First switch the sample pointers: no need to copy */
261 tmp = cpu_prev_jif;
262 cpu_prev_jif = cpu_jif;
263 cpu_jif = tmp;
264
265 /* Get the new samples */
266 for (i = 0; i < num_cpus; i++)
267 read_cpu_jiffy(fp, &cpu_jif[i]);
173 } 268 }
269#endif
174 fclose(fp); 270 fclose(fp);
175 jif.total = jif.usr + jif.nic + jif.sys + jif.idle
176 + jif.iowait + jif.irq + jif.softirq + jif.steal;
177 /* procps 2.x does not count iowait as busy time */
178 jif.busy = jif.total - jif.idle - jif.iowait;
179} 271}
180 272
181
182static void do_stats(void) 273static void do_stats(void)
183{ 274{
184 top_status_t *cur; 275 top_status_t *cur;
@@ -189,7 +280,7 @@ static void do_stats(void)
189 get_jiffy_counts(); 280 get_jiffy_counts();
190 total_pcpu = 0; 281 total_pcpu = 0;
191 /* total_vsz = 0; */ 282 /* total_vsz = 0; */
192 new_hist = xmalloc(sizeof(struct save_hist)*ntop); 283 new_hist = xmalloc(sizeof(new_hist[0]) * ntop);
193 /* 284 /*
194 * Make a pass through the data to get stats. 285 * Make a pass through the data to get stats.
195 */ 286 */
@@ -230,6 +321,7 @@ static void do_stats(void)
230 prev_hist = new_hist; 321 prev_hist = new_hist;
231 prev_hist_count = ntop; 322 prev_hist_count = ntop;
232} 323}
324
233#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ 325#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
234 326
235#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS 327#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)
257} 349}
258#endif 350#endif
259 351
260static unsigned long display_header(int scr_width) 352#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
353static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
354{
355 /*
356 * xxx% = (jif.xxx - prev_jif.xxx) / (jif.total - prev_jif.total) * 100%
357 */
358 unsigned total_diff;
359 jiffy_counts_t *p_jif, *p_prev_jif;
360 int i;
361
362#if ENABLE_FEATURE_TOP_SMP_CPU
363 int n_cpu_lines;
364#endif
365
366 /* using (unsigned) casts to make operations cheaper */
367#define CALC_TOT_DIFF ((unsigned)(p_jif->total - p_prev_jif->total) ? : 1)
368
369#if ENABLE_FEATURE_TOP_DECIMALS
370#define CALC_STAT(xxx) char xxx[8]
371#define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(p_jif->xxx - p_prev_jif->xxx), total_diff)
372#define FMT "%s"
373#else
374#define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(p_jif->xxx - p_prev_jif->xxx) / total_diff
375#define SHOW_STAT(xxx) xxx
376#define FMT "%4u%% "
377#endif
378
379#if !ENABLE_FEATURE_TOP_SMP_CPU
380 {
381 i = 1;
382 p_jif = &jif;
383 p_prev_jif = &prev_jif;
384#else
385 /* Loop thru CPU(s) */
386 n_cpu_lines = smp_cpu_info ? num_cpus : 1;
387 if (n_cpu_lines > *lines_rem_p)
388 n_cpu_lines = *lines_rem_p;
389
390 for (i = 0; i < n_cpu_lines; i++) {
391 /* set the real loop end */
392 p_jif = &cpu_jif[i];
393 p_prev_jif = &cpu_prev_jif[i];
394#endif
395 total_diff = CALC_TOT_DIFF;
396
397 { /* Need block: CALC_STAT are declarations */
398 CALC_STAT(usr);
399 CALC_STAT(sys);
400 CALC_STAT(nic);
401 CALC_STAT(idle);
402 CALC_STAT(iowait);
403 CALC_STAT(irq);
404 CALC_STAT(softirq);
405 /*CALC_STAT(steal);*/
406
407 snprintf(scrbuf, scr_width,
408 /* Barely fits in 79 chars when in "decimals" mode. */
409#if ENABLE_FEATURE_TOP_SMP_CPU
410 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
411 (smp_cpu_info ? utoa(i) : ""),
412#else
413 "CPU:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
414#endif
415 SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle),
416 SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq)
417 /*, SHOW_STAT(steal) - what is this 'steal' thing? */
418 /* I doubt anyone wants to know it */
419 );
420 puts(scrbuf);
421 }
422 }
423#undef SHOW_STAT
424#undef CALC_STAT
425#undef FMT
426 *lines_rem_p -= i;
427}
428#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */
429#define display_cpus(scr_width, scrbuf, lines_rem) ((void)0)
430#endif
431
432static unsigned long display_header(int scr_width, int *lines_rem_p)
261{ 433{
262 FILE *fp; 434 FILE *fp;
263 char buf[80]; 435 char buf[80];
264 char scrbuf[80]; 436 char scrbuf[80];
265 unsigned long total, used, mfree, shared, buffers, cached; 437 unsigned long total, used, mfree, shared, buffers, cached;
266#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
267 unsigned total_diff;
268#endif
269 438
270 /* read memory info */ 439 /* read memory info */
271 fp = xfopen_for_read("meminfo"); 440 fp = xfopen_for_read("meminfo");
@@ -298,7 +467,6 @@ static unsigned long display_header(int scr_width)
298 * sizes in kilobytes. This should be safe for both 2.4 and 467 * sizes in kilobytes. This should be safe for both 2.4 and
299 * 2.6. 468 * 2.6.
300 */ 469 */
301
302 fscanf(fp, "MemFree: %lu %s\n", &mfree, buf); 470 fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);
303 471
304 /* 472 /*
@@ -323,47 +491,12 @@ static unsigned long display_header(int scr_width)
323 used, mfree, shared, buffers, cached); 491 used, mfree, shared, buffers, cached);
324 /* clear screen & go to top */ 492 /* clear screen & go to top */
325 printf(OPT_BATCH_MODE ? "%s\n" : "\e[H\e[J%s\n", scrbuf); 493 printf(OPT_BATCH_MODE ? "%s\n" : "\e[H\e[J%s\n", scrbuf);
494 (*lines_rem_p)--;
326 495
327#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS 496 /* Display CPU time split as percentage of total time
328 /* 497 * This displays either a cumulative line or one line per CPU
329 * xxx% = (jif.xxx - prev_jif.xxx) / (jif.total - prev_jif.total) * 100%
330 */ 498 */
331 /* using (unsigned) casts to make operations cheaper */ 499 display_cpus(scr_width, scrbuf, lines_rem_p);
332 total_diff = ((unsigned)(jif.total - prev_jif.total) ? : 1);
333#if ENABLE_FEATURE_TOP_DECIMALS
334/* Generated code is approx +0.3k */
335#define CALC_STAT(xxx) char xxx[8]
336#define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(jif.xxx - prev_jif.xxx), total_diff)
337#define FMT "%s"
338#else
339#define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(jif.xxx - prev_jif.xxx) / total_diff
340#define SHOW_STAT(xxx) xxx
341#define FMT "%4u%% "
342#endif
343 { /* need block: CALC_STAT are declarations */
344 CALC_STAT(usr);
345 CALC_STAT(sys);
346 CALC_STAT(nic);
347 CALC_STAT(idle);
348 CALC_STAT(iowait);
349 CALC_STAT(irq);
350 CALC_STAT(softirq);
351 //CALC_STAT(steal);
352
353 snprintf(scrbuf, scr_width,
354 /* Barely fits in 79 chars when in "decimals" mode. */
355 "CPU:"FMT"usr"FMT"sys"FMT"nice"FMT"idle"FMT"io"FMT"irq"FMT"softirq",
356 SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle),
357 SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq)
358 //, SHOW_STAT(steal) - what is this 'steal' thing?
359 // I doubt anyone wants to know it
360 );
361 }
362 puts(scrbuf);
363#undef SHOW_STAT
364#undef CALC_STAT
365#undef FMT
366#endif
367 500
368 /* read load average as a string */ 501 /* read load average as a string */
369 buf[0] = '\0'; 502 buf[0] = '\0';
@@ -371,35 +504,39 @@ static unsigned long display_header(int scr_width)
371 buf[sizeof("N.NN N.NN N.NN")-1] = '\0'; 504 buf[sizeof("N.NN N.NN N.NN")-1] = '\0';
372 snprintf(scrbuf, scr_width, "Load average: %s", buf); 505 snprintf(scrbuf, scr_width, "Load average: %s", buf);
373 puts(scrbuf); 506 puts(scrbuf);
507 (*lines_rem_p)--;
374 508
375 return total; 509 return total;
376} 510}
377 511
378static NOINLINE void display_process_list(int count, int scr_width) 512static NOINLINE void display_process_list(int lines_rem, int scr_width)
379{ 513{
380 enum { 514 enum {
381 BITS_PER_INT = sizeof(int)*8 515 BITS_PER_INT = sizeof(int) * 8
382 }; 516 };
383 517
384 top_status_t *s = top; 518 top_status_t *s;
385 char vsz_str_buf[8]; 519 char vsz_str_buf[8];
386 unsigned long total_memory = display_header(scr_width); /* or use total_vsz? */ 520 unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */
387 /* xxx_shift and xxx_scale variables allow us to replace 521 /* xxx_shift and xxx_scale variables allow us to replace
388 * expensive divides with multiply and shift */ 522 * expensive divides with multiply and shift */
389 unsigned pmem_shift, pmem_scale, pmem_half; 523 unsigned pmem_shift, pmem_scale, pmem_half;
390#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 524#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
391 unsigned pcpu_shift, pcpu_scale, pcpu_half; 525 unsigned pcpu_shift, pcpu_scale, pcpu_half;
392 unsigned busy_jifs; 526 unsigned busy_jifs;
527#endif
393 528
394 /* what info of the processes is shown */ 529 /* what info of the processes is shown */
395 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, 530 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,
396 " PID PPID USER STAT VSZ %MEM %CPU COMMAND"); 531 " PID PPID USER STAT VSZ %MEM"
397#else 532#if ENABLE_FEATURE_TOP_SMP_PROCESS
398 533 " CPU"
399 /* !CPU_USAGE_PERCENTAGE */ 534#endif
400 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, 535#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
401 " PID PPID USER STAT VSZ %MEM COMMAND"); 536 " %CPU"
402#endif 537#endif
538 " COMMAND");
539 lines_rem--;
403 540
404#if ENABLE_FEATURE_TOP_DECIMALS 541#if ENABLE_FEATURE_TOP_DECIMALS
405#define UPSCALE 1000 542#define UPSCALE 1000
@@ -453,9 +590,12 @@ static NOINLINE void display_process_list(int count, int scr_width)
453 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ 590 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
454#endif 591#endif
455 592
456 scr_width += 2; /* account for leading '\n' and trailing NUL */
457 /* Ok, all preliminary data is ready, go through the list */ 593 /* Ok, all preliminary data is ready, go through the list */
458 while (count-- > 0) { 594 scr_width += 2; /* account for leading '\n' and trailing NUL */
595 if (lines_rem > ntop)
596 lines_rem = ntop;
597 s = top;
598 while (--lines_rem >= 0) {
459 unsigned col; 599 unsigned col;
460 CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift); 600 CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
461#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 601#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
@@ -466,9 +606,12 @@ static NOINLINE void display_process_list(int count, int scr_width)
466 sprintf(vsz_str_buf, "%6ldm", s->vsz/1024); 606 sprintf(vsz_str_buf, "%6ldm", s->vsz/1024);
467 else 607 else
468 sprintf(vsz_str_buf, "%7ld", s->vsz); 608 sprintf(vsz_str_buf, "%7ld", s->vsz);
469 // PID PPID USER STAT VSZ %MEM [%CPU] COMMAND 609 /* PID PPID USER STAT VSZ %MEM [%CPU] COMMAND */
470 col = snprintf(line_buf, scr_width, 610 col = snprintf(line_buf, scr_width,
471 "\n" "%5u%6u %-8.8s %s%s" FMT 611 "\n" "%5u%6u %-8.8s %s%s" FMT
612#if ENABLE_FEATURE_TOP_SMP_PROCESS
613 " %3d"
614#endif
472#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 615#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
473 FMT 616 FMT
474#endif 617#endif
@@ -476,6 +619,9 @@ static NOINLINE void display_process_list(int count, int scr_width)
476 s->pid, s->ppid, get_cached_username(s->uid), 619 s->pid, s->ppid, get_cached_username(s->uid),
477 s->state, vsz_str_buf, 620 s->state, vsz_str_buf,
478 SHOW_STAT(pmem) 621 SHOW_STAT(pmem)
622#if ENABLE_FEATURE_TOP_SMP_PROCESS
623 , s->last_seen_on_cpu
624#endif
479#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 625#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
480 , SHOW_STAT(pcpu) 626 , SHOW_STAT(pcpu)
481#endif 627#endif
@@ -580,7 +726,7 @@ static char *grab_number(char *str, const char *match, unsigned sz)
580} 726}
581 727
582/* display header info (meminfo / loadavg) */ 728/* display header info (meminfo / loadavg) */
583static void display_topmem_header(int scr_width) 729static void display_topmem_header(int scr_width, int *lines_rem_p)
584{ 730{
585 char linebuf[128]; 731 char linebuf[128];
586 unsigned i; 732 unsigned i;
@@ -655,6 +801,8 @@ static void display_topmem_header(int scr_width)
655 "Swap %stotal %sfree", // TODO: % used? 801 "Swap %stotal %sfree", // TODO: % used?
656 S(swaptotal), S(swapfree)); 802 S(swaptotal), S(swapfree));
657 printf("%.*s\n", scr_width, linebuf); 803 printf("%.*s\n", scr_width, linebuf);
804
805 (*lines_rem_p) -= 3;
658#undef S 806#undef S
659 807
660 for (i = 0; i < ARRAY_SIZE(str); i++) 808 for (i = 0; i < ARRAY_SIZE(str); i++)
@@ -680,19 +828,22 @@ static void ulltoa6_and_space(unsigned long long ul, char buf[6])
680 buf[5] = ' '; 828 buf[5] = ' ';
681} 829}
682 830
683static NOINLINE void display_topmem_process_list(int count, int scr_width) 831static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width)
684{ 832{
685#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK" 833#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK"
686#define MIN_WIDTH sizeof(HDR_STR) 834#define MIN_WIDTH sizeof(HDR_STR)
687 const topmem_status_t *s = topmem; 835 const topmem_status_t *s = topmem;
688 836
689 display_topmem_header(scr_width); 837 display_topmem_header(scr_width, &lines_rem);
690 strcpy(line_buf, HDR_STR " COMMAND"); 838 strcpy(line_buf, HDR_STR " COMMAND");
691 line_buf[5 + sort_field * 6] = '*'; 839 line_buf[5 + sort_field * 6] = '*';
692 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf); 840 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf);
841 lines_rem--;
693 842
694 while (--count >= 0) { 843 if (lines_rem > ntop)
695 // PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND 844 lines_rem = ntop;
845 while (--lines_rem >= 0) {
846 /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
696 ulltoa6_and_space(s->pid , &line_buf[0*6]); 847 ulltoa6_and_space(s->pid , &line_buf[0*6]);
697 ulltoa6_and_space(s->vsz , &line_buf[1*6]); 848 ulltoa6_and_space(s->vsz , &line_buf[1*6]);
698 ulltoa6_and_space(s->vszrw , &line_buf[2*6]); 849 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)
714#undef MIN_WIDTH 865#undef MIN_WIDTH
715} 866}
716#else 867#else
717void display_topmem_process_list(int count, int scr_width); 868void display_topmem_process_list(int lines_rem, int scr_width);
718int topmem_sort(char *a, char *b); 869int topmem_sort(char *a, char *b);
719#endif /* TOPMEM */ 870#endif /* TOPMEM */
720 871
@@ -731,6 +882,9 @@ enum {
731 | PSSCAN_UTIME 882 | PSSCAN_UTIME
732 | PSSCAN_STATE 883 | PSSCAN_STATE
733 | PSSCAN_COMM 884 | PSSCAN_COMM
885#if ENABLE_FEATURE_TOP_SMP_PROCESS
886 | PSSCAN_CPU
887#endif
734 | PSSCAN_UIDGID, 888 | PSSCAN_UIDGID,
735 TOPMEM_MASK = 0 889 TOPMEM_MASK = 0
736 | PSSCAN_PID 890 | PSSCAN_PID
@@ -741,9 +895,9 @@ enum {
741int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 895int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
742int top_main(int argc UNUSED_PARAM, char **argv) 896int top_main(int argc UNUSED_PARAM, char **argv)
743{ 897{
744 int count;
745 int iterations; 898 int iterations;
746 unsigned lines, col; 899 unsigned lines, col;
900 int lines_rem;
747 unsigned interval; 901 unsigned interval;
748 char *sinterval; 902 char *sinterval;
749 SKIP_FEATURE_TOPMEM(const) unsigned scan_mask = TOP_MASK; 903 SKIP_FEATURE_TOPMEM(const) unsigned scan_mask = TOP_MASK;
@@ -760,12 +914,18 @@ int top_main(int argc UNUSED_PARAM, char **argv)
760 914
761 interval = 5; /* default update interval is 5 seconds */ 915 interval = 5; /* default update interval is 5 seconds */
762 iterations = 0; /* infinite */ 916 iterations = 0; /* infinite */
917#if ENABLE_FEATURE_TOP_SMP_CPU
918 /*num_cpus = 0;*/
919 /*smp_cpu_info = 0;*/ /* to start with show aggregate */
920 cpu_jif = &jif;
921 cpu_prev_jif = &prev_jif;
922#endif
763 923
764 /* all args are options; -n NUM */ 924 /* all args are options; -n NUM */
765 opt_complementary = "-:n+"; 925 opt_complementary = "-:n+";
766 if (getopt32(argv, "d:n:b", &sinterval, &iterations) & OPT_d) { 926 if (getopt32(argv, "d:n:b", &sinterval, &iterations) & OPT_d) {
767 /* Need to limit it to not overflow poll timeout */ 927 /* Need to limit it to not overflow poll timeout */
768 interval = xatou16(sinterval); // -d 928 interval = xatou16(sinterval); /* -d */
769 } 929 }
770 930
771 /* change to /proc */ 931 /* change to /proc */
@@ -803,10 +963,6 @@ int top_main(int argc UNUSED_PARAM, char **argv)
803#endif /* FEATURE_USE_TERMIOS */ 963#endif /* FEATURE_USE_TERMIOS */
804 if (col > LINE_BUF_SIZE-2) /* +2 bytes for '\n', NUL, */ 964 if (col > LINE_BUF_SIZE-2) /* +2 bytes for '\n', NUL, */
805 col = LINE_BUF_SIZE-2; 965 col = LINE_BUF_SIZE-2;
806 if (!ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && scan_mask == TOP_MASK)
807 lines -= 3;
808 else
809 lines -= 4;
810 966
811 /* read process IDs & status for all the processes */ 967 /* read process IDs & status for all the processes */
812 while ((p = procps_scan(p, scan_mask)) != NULL) { 968 while ((p = procps_scan(p, scan_mask)) != NULL) {
@@ -823,6 +979,9 @@ int top_main(int argc UNUSED_PARAM, char **argv)
823 top[n].uid = p->uid; 979 top[n].uid = p->uid;
824 strcpy(top[n].state, p->state); 980 strcpy(top[n].state, p->state);
825 strcpy(top[n].comm, p->comm); 981 strcpy(top[n].comm, p->comm);
982#if ENABLE_FEATURE_TOP_SMP_PROCESS
983 top[n].last_seen_on_cpu = p->last_seen_on_cpu;
984#endif
826 } else { /* TOPMEM */ 985 } else { /* TOPMEM */
827#if ENABLE_FEATURE_TOPMEM 986#if ENABLE_FEATURE_TOPMEM
828 if (!(p->mapped_ro | p->mapped_rw)) 987 if (!(p->mapped_ro | p->mapped_rw))
@@ -856,7 +1015,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
856 continue; 1015 continue;
857 } 1016 }
858 do_stats(); 1017 do_stats();
859/* TODO: we don't need to sort all 10000 processes, we need to find top 24! */ 1018 /* TODO: we don't need to sort all 10000 processes, we need to find top 24! */
860 qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp); 1019 qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
861#else 1020#else
862 qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0])); 1021 qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
@@ -867,15 +1026,15 @@ int top_main(int argc UNUSED_PARAM, char **argv)
867 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); 1026 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
868 } 1027 }
869#endif 1028#endif
870 count = lines; 1029 lines_rem = lines;
871 if (OPT_BATCH_MODE || count > ntop) { 1030 if (OPT_BATCH_MODE) {
872 count = ntop; 1031 lines_rem = INT_MAX;
873 } 1032 }
874 if (scan_mask == TOP_MASK) 1033 if (scan_mask == TOP_MASK)
875 display_process_list(count, col); 1034 display_process_list(lines_rem, col);
876#if ENABLE_FEATURE_TOPMEM 1035#if ENABLE_FEATURE_TOPMEM
877 else 1036 else
878 display_topmem_process_list(count, col); 1037 display_topmem_process_list(lines_rem, col);
879#endif 1038#endif
880 clearmems(); 1039 clearmems();
881 if (iterations >= 0 && !--iterations) 1040 if (iterations >= 0 && !--iterations)
@@ -932,6 +1091,23 @@ int top_main(int argc UNUSED_PARAM, char **argv)
932 if (c == 'r') 1091 if (c == 'r')
933 inverted ^= 1; 1092 inverted ^= 1;
934#endif 1093#endif
1094#if ENABLE_FEATURE_TOP_SMP_CPU
1095 if (c == 'c') { /* procps-2.0.18 uses 'C' */
1096 /* User wants to toggle per cpu <> aggregate */
1097 if (smp_cpu_info) {
1098 free(cpu_prev_jif);
1099 free(cpu_jif);
1100 cpu_jif = &jif;
1101 cpu_prev_jif = &prev_jif;
1102 } else {
1103 /* Prepare for xrealloc() */
1104 cpu_jif = cpu_prev_jif = NULL;
1105 }
1106 num_cpus = 0;
1107 smp_cpu_info = !smp_cpu_info;
1108 get_jiffy_counts();
1109 }
1110#endif
935#endif 1111#endif
936 } 1112 }
937#endif /* FEATURE_USE_TERMIOS */ 1113#endif /* FEATURE_USE_TERMIOS */