source: MondoRescue/branches/3.3/mindi-busybox/procps/top.c@ 3837

Last change on this file since 3837 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 32.6 KB
RevLine 
[821]1/* vi: set sw=4 ts=4: */
2/*
3 * A tiny 'top' utility.
4 *
5 * This is written specifically for the linux /proc/<PID>/stat(m)
6 * files format.
[2725]7 *
[821]8 * This reads the PIDs of all processes and their status and shows
9 * the status of processes (first ones that fit to screen) at given
10 * intervals.
11 *
12 * NOTES:
13 * - At startup this changes to /proc, all the reads are then
14 * relative to that.
15 *
16 * (C) Eero Tamminen <oak at welho dot com>
17 *
18 * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
[2725]19 *
20 * Sept 2008: Vineet Gupta <vineet.gupta@arc.com>
21 * Added Support for reporting SMP Information
[3232]22 * - CPU where process was last seen running
[2725]23 * (to see effect of sched_setaffinity() etc)
[3232]24 * - CPU time split (idle/IO/wait etc) per CPU
[2725]25 *
[821]26 * Copyright (c) 1992 Branko Lankester
27 * Copyright (c) 1992 Roger Binns
28 * Copyright (C) 1994-1996 Charles L. Blake.
29 * Copyright (C) 1992-1998 Michael K. Johnson
[2725]30 *
31 * Licensed under GPLv2, see file LICENSE in this source tree.
[821]32 */
[3232]33/* How to snapshot /proc for debugging top problems:
34 * for f in /proc/[0-9]*""/stat; do
35 * n=${f#/proc/}
36 * n=${n%/stat}_stat
37 * cp $f $n
38 * done
39 * cp /proc/stat /proc/meminfo /proc/loadavg .
40 * top -bn1 >top.out
41 *
42 * ...and how to run top on it on another machine:
43 * rm -rf proc; mkdir proc
44 * for f in [0-9]*_stat; do
45 * p=${f%_stat}
46 * mkdir -p proc/$p
47 * cp $f proc/$p/stat
48 * done
49 * cp stat meminfo loadavg proc
50 * chroot . ./top -bn1 >top1.out
51 */
[821]52
[3232]53//config:config TOP
54//config: bool "top"
55//config: default y
56//config: help
57//config: The top program provides a dynamic real-time view of a running
58//config: system.
59//config:
60//config:config FEATURE_TOP_CPU_USAGE_PERCENTAGE
61//config: bool "Show CPU per-process usage percentage"
62//config: default y
63//config: depends on TOP
64//config: help
65//config: Make top display CPU usage for each process.
66//config: This adds about 2k.
67//config:
68//config:config FEATURE_TOP_CPU_GLOBAL_PERCENTS
69//config: bool "Show CPU global usage percentage"
70//config: default y
71//config: depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE
72//config: help
73//config: Makes top display "CPU: NN% usr NN% sys..." line.
74//config: This adds about 0.5k.
75//config:
76//config:config FEATURE_TOP_SMP_CPU
77//config: bool "SMP CPU usage display ('c' key)"
78//config: default y
79//config: depends on FEATURE_TOP_CPU_GLOBAL_PERCENTS
80//config: help
81//config: Allow 'c' key to switch between individual/cumulative CPU stats
82//config: This adds about 0.5k.
83//config:
84//config:config FEATURE_TOP_DECIMALS
85//config: bool "Show 1/10th of a percent in CPU/mem statistics"
86//config: default y
87//config: depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE
88//config: help
89//config: Show 1/10th of a percent in CPU/mem statistics.
90//config: This adds about 0.3k.
91//config:
92//config:config FEATURE_TOP_SMP_PROCESS
93//config: bool "Show CPU process runs on ('j' field)"
94//config: default y
95//config: depends on TOP
96//config: help
97//config: Show CPU where process was last found running on.
98//config: This is the 'j' field.
99//config:
100//config:config FEATURE_TOPMEM
101//config: bool "Topmem command ('s' key)"
102//config: default y
103//config: depends on TOP
104//config: help
105//config: Enable 's' in top (gives lots of memory info).
106
[1765]107#include "libbb.h"
[3621]108#include "common_bufsiz.h"
[821]109
110
[1765]111typedef struct top_status_t {
112 unsigned long vsz;
113#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
114 unsigned long ticks;
115 unsigned pcpu; /* delta of ticks */
[821]116#endif
[1765]117 unsigned pid, ppid;
118 unsigned uid;
119 char state[4];
120 char comm[COMM_LEN];
[2725]121#if ENABLE_FEATURE_TOP_SMP_PROCESS
122 int last_seen_on_cpu;
123#endif
[1765]124} top_status_t;
[821]125
[1765]126typedef struct jiffy_counts_t {
[2725]127 /* Linux 2.4.x has only first four */
128 unsigned long long usr, nic, sys, idle;
129 unsigned long long iowait, irq, softirq, steal;
[1765]130 unsigned long long total;
131 unsigned long long busy;
132} jiffy_counts_t;
[821]133
[1765]134/* This structure stores some critical information from one frame to
135 the next. Used for finding deltas. */
136typedef struct save_hist {
137 unsigned long ticks;
[2725]138 pid_t pid;
[1765]139} save_hist;
[821]140
[1765]141typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
[821]142
[2725]143
[1765]144enum { SORT_DEPTH = 3 };
145
[2725]146
[1765]147struct globals {
148 top_status_t *top;
149 int ntop;
[3232]150 smallint inverted;
[2725]151#if ENABLE_FEATURE_TOPMEM
152 smallint sort_field;
153#endif
154#if ENABLE_FEATURE_TOP_SMP_CPU
155 smallint smp_cpu_info; /* one/many cpu info lines? */
156#endif
[3232]157 unsigned lines; /* screen height */
[1765]158#if ENABLE_FEATURE_USE_TERMIOS
159 struct termios initial_settings;
[3232]160 int scroll_ofs;
161#define G_scroll_ofs G.scroll_ofs
162#else
163#define G_scroll_ofs 0
[1765]164#endif
165#if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
[2725]166 cmp_funcp sort_function[1];
[1765]167#else
168 cmp_funcp sort_function[SORT_DEPTH];
169 struct save_hist *prev_hist;
170 int prev_hist_count;
[2725]171 jiffy_counts_t cur_jif, prev_jif;
[1765]172 /* int hist_iterations; */
173 unsigned total_pcpu;
174 /* unsigned long total_vsz; */
175#endif
[2725]176#if ENABLE_FEATURE_TOP_SMP_CPU
177 /* Per CPU samples: current and last */
178 jiffy_counts_t *cpu_jif, *cpu_prev_jif;
179 int num_cpus;
180#endif
[3232]181#if ENABLE_FEATURE_USE_TERMIOS
182 char kbd_input[KEYCODE_BUFFER_SIZE];
183#endif
[2725]184 char line_buf[80];
185}; //FIX_ALIASING; - large code growth
186enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) };
[3621]187#define G (*(struct globals*)bb_common_bufsiz1)
[1765]188#define top (G.top )
189#define ntop (G.ntop )
[2725]190#define sort_field (G.sort_field )
191#define inverted (G.inverted )
192#define smp_cpu_info (G.smp_cpu_info )
193#define initial_settings (G.initial_settings )
[1765]194#define sort_function (G.sort_function )
195#define prev_hist (G.prev_hist )
196#define prev_hist_count (G.prev_hist_count )
[2725]197#define cur_jif (G.cur_jif )
[1765]198#define prev_jif (G.prev_jif )
[2725]199#define cpu_jif (G.cpu_jif )
200#define cpu_prev_jif (G.cpu_prev_jif )
201#define num_cpus (G.num_cpus )
[1765]202#define total_pcpu (G.total_pcpu )
[2725]203#define line_buf (G.line_buf )
[3621]204#define INIT_G() do { \
205 setup_common_bufsiz(); \
206 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
207 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \
208} while (0)
[1765]209
[2725]210enum {
211 OPT_d = (1 << 0),
212 OPT_n = (1 << 1),
213 OPT_b = (1 << 2),
214 OPT_m = (1 << 3),
215 OPT_EOF = (1 << 4), /* pseudo: "we saw EOF in stdin" */
216};
217#define OPT_BATCH_MODE (option_mask32 & OPT_b)
[1765]218
219
220#if ENABLE_FEATURE_USE_TERMIOS
221static int pid_sort(top_status_t *P, top_status_t *Q)
[821]222{
[1765]223 /* Buggy wrt pids with high bit set */
224 /* (linux pids are in [1..2^15-1]) */
[821]225 return (Q->pid - P->pid);
226}
227#endif
228
[1765]229static int mem_sort(top_status_t *P, top_status_t *Q)
[821]230{
[1765]231 /* We want to avoid unsigned->signed and truncation errors */
232 if (Q->vsz < P->vsz) return -1;
233 return Q->vsz != P->vsz; /* 0 if ==, 1 if > */
[821]234}
235
236
[1765]237#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
[821]238
[1765]239static int pcpu_sort(top_status_t *P, top_status_t *Q)
[821]240{
[1765]241 /* Buggy wrt ticks with high bit set */
242 /* Affects only processes for which ticks overflow */
243 return (int)Q->pcpu - (int)P->pcpu;
[821]244}
245
[1765]246static int time_sort(top_status_t *P, top_status_t *Q)
[821]247{
[1765]248 /* We want to avoid unsigned->signed and truncation errors */
249 if (Q->ticks < P->ticks) return -1;
250 return Q->ticks != P->ticks; /* 0 if ==, 1 if > */
[821]251}
252
[1765]253static int mult_lvl_cmp(void* a, void* b)
254{
[821]255 int i, cmp_val;
256
[1765]257 for (i = 0; i < SORT_DEPTH; i++) {
[821]258 cmp_val = (*sort_function[i])(a, b);
259 if (cmp_val != 0)
[3232]260 break;
[821]261 }
[3232]262 return inverted ? -cmp_val : cmp_val;
[821]263}
264
[2725]265static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
266{
267#if !ENABLE_FEATURE_TOP_SMP_CPU
[3621]268 static const char fmt[] ALIGN1 = "cpu %llu %llu %llu %llu %llu %llu %llu %llu";
[2725]269#else
[3621]270 static const char fmt[] ALIGN1 = "cp%*s %llu %llu %llu %llu %llu %llu %llu %llu";
[2725]271#endif
272 int ret;
[821]273
[2725]274 if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */)
275 return 0;
276 ret = sscanf(line_buf, fmt,
277 &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle,
278 &p_jif->iowait, &p_jif->irq, &p_jif->softirq,
279 &p_jif->steal);
280 if (ret >= 4) {
281 p_jif->total = p_jif->usr + p_jif->nic + p_jif->sys + p_jif->idle
282 + p_jif->iowait + p_jif->irq + p_jif->softirq + p_jif->steal;
283 /* procps 2.x does not count iowait as busy time */
284 p_jif->busy = p_jif->total - p_jif->idle - p_jif->iowait;
285 }
286
287 return ret;
288}
289
[821]290static void get_jiffy_counts(void)
291{
[2725]292 FILE* fp = xfopen_for_read("stat");
293
294 /* We need to parse cumulative counts even if SMP CPU display is on,
295 * they are used to calculate per process CPU% */
296 prev_jif = cur_jif;
297 if (read_cpu_jiffy(fp, &cur_jif) < 4)
[3621]298 bb_error_msg_and_die("can't read '%s'", "/proc/stat");
[2725]299
300#if !ENABLE_FEATURE_TOP_SMP_CPU
301 fclose(fp);
302 return;
303#else
304 if (!smp_cpu_info) {
305 fclose(fp);
306 return;
[821]307 }
[2725]308
309 if (!num_cpus) {
310 /* First time here. How many CPUs?
311 * There will be at least 1 /proc/stat line with cpu%d
312 */
313 while (1) {
314 cpu_jif = xrealloc_vector(cpu_jif, 1, num_cpus);
315 if (read_cpu_jiffy(fp, &cpu_jif[num_cpus]) <= 4)
316 break;
317 num_cpus++;
318 }
319 if (num_cpus == 0) /* /proc/stat with only "cpu ..." line?! */
320 smp_cpu_info = 0;
321
322 cpu_prev_jif = xzalloc(sizeof(cpu_prev_jif[0]) * num_cpus);
323
324 /* Otherwise the first per cpu display shows all 100% idles */
325 usleep(50000);
326 } else { /* Non first time invocation */
327 jiffy_counts_t *tmp;
328 int i;
329
330 /* First switch the sample pointers: no need to copy */
331 tmp = cpu_prev_jif;
332 cpu_prev_jif = cpu_jif;
333 cpu_jif = tmp;
334
335 /* Get the new samples */
336 for (i = 0; i < num_cpus; i++)
337 read_cpu_jiffy(fp, &cpu_jif[i]);
338 }
339#endif
[821]340 fclose(fp);
341}
342
343static void do_stats(void)
344{
[1765]345 top_status_t *cur;
346 pid_t pid;
347 int i, last_i, n;
[821]348 struct save_hist *new_hist;
349
350 get_jiffy_counts();
351 total_pcpu = 0;
[1765]352 /* total_vsz = 0; */
[2725]353 new_hist = xmalloc(sizeof(new_hist[0]) * ntop);
[821]354 /*
355 * Make a pass through the data to get stats.
356 */
357 /* hist_iterations = 0; */
358 i = 0;
359 for (n = 0; n < ntop; n++) {
360 cur = top + n;
361
362 /*
363 * Calculate time in cur process. Time is sum of user time
364 * and system time
365 */
366 pid = cur->pid;
[1765]367 new_hist[n].ticks = cur->ticks;
[821]368 new_hist[n].pid = pid;
369
370 /* find matching entry from previous pass */
371 cur->pcpu = 0;
372 /* do not start at index 0, continue at last used one
373 * (brought hist_iterations from ~14000 down to 172) */
374 last_i = i;
375 if (prev_hist_count) do {
376 if (prev_hist[i].pid == pid) {
[1765]377 cur->pcpu = cur->ticks - prev_hist[i].ticks;
378 total_pcpu += cur->pcpu;
[821]379 break;
380 }
381 i = (i+1) % prev_hist_count;
382 /* hist_iterations++; */
383 } while (i != last_i);
[1765]384 /* total_vsz += cur->vsz; */
[821]385 }
386
387 /*
388 * Save cur frame's information.
389 */
390 free(prev_hist);
391 prev_hist = new_hist;
392 prev_hist_count = ntop;
393}
[2725]394
[1765]395#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
[821]396
[1765]397#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
398/* formats 7 char string (8 with terminating NUL) */
399static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
400{
401 unsigned t;
402 if (value >= total) { /* 100% ? */
403 strcpy(pbuf, " 100% ");
404 return pbuf;
405 }
406 /* else generate " [N/space]N.N% " string */
407 value = 1000 * value / total;
408 t = value / 100;
409 value = value % 100;
410 pbuf[0] = ' ';
411 pbuf[1] = t ? t + '0' : ' ';
412 pbuf[2] = '0' + (value / 10);
413 pbuf[3] = '.';
414 pbuf[4] = '0' + (value % 10);
415 pbuf[5] = '%';
416 pbuf[6] = ' ';
417 pbuf[7] = '\0';
418 return pbuf;
419}
420#endif
421
[2725]422#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
423static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
[821]424{
[2725]425 /*
426 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100%
427 */
428 unsigned total_diff;
429 jiffy_counts_t *p_jif, *p_prev_jif;
430 int i;
431# if ENABLE_FEATURE_TOP_SMP_CPU
432 int n_cpu_lines;
433# endif
434
435 /* using (unsigned) casts to make operations cheaper */
436# define CALC_TOTAL_DIFF do { \
437 total_diff = (unsigned)(p_jif->total - p_prev_jif->total); \
438 if (total_diff == 0) total_diff = 1; \
439} while (0)
440
441# if ENABLE_FEATURE_TOP_DECIMALS
442# define CALC_STAT(xxx) char xxx[8]
443# define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(p_jif->xxx - p_prev_jif->xxx), total_diff)
444# define FMT "%s"
445# else
446# define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(p_jif->xxx - p_prev_jif->xxx) / total_diff
447# define SHOW_STAT(xxx) xxx
448# define FMT "%4u%% "
449# endif
450
451# if !ENABLE_FEATURE_TOP_SMP_CPU
452 {
453 i = 1;
454 p_jif = &cur_jif;
455 p_prev_jif = &prev_jif;
456# else
457 /* Loop thru CPU(s) */
458 n_cpu_lines = smp_cpu_info ? num_cpus : 1;
459 if (n_cpu_lines > *lines_rem_p)
460 n_cpu_lines = *lines_rem_p;
461
462 for (i = 0; i < n_cpu_lines; i++) {
463 p_jif = &cpu_jif[i];
464 p_prev_jif = &cpu_prev_jif[i];
465# endif
466 CALC_TOTAL_DIFF;
467
468 { /* Need a block: CALC_STAT are declarations */
469 CALC_STAT(usr);
470 CALC_STAT(sys);
471 CALC_STAT(nic);
472 CALC_STAT(idle);
473 CALC_STAT(iowait);
474 CALC_STAT(irq);
475 CALC_STAT(softirq);
476 /*CALC_STAT(steal);*/
477
478 snprintf(scrbuf, scr_width,
479 /* Barely fits in 79 chars when in "decimals" mode. */
480# if ENABLE_FEATURE_TOP_SMP_CPU
481 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
482 (smp_cpu_info ? utoa(i) : ""),
483# else
484 "CPU:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
485# endif
486 SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle),
487 SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq)
488 /*, SHOW_STAT(steal) - what is this 'steal' thing? */
489 /* I doubt anyone wants to know it */
490 );
491 puts(scrbuf);
492 }
493 }
494# undef SHOW_STAT
495# undef CALC_STAT
496# undef FMT
497 *lines_rem_p -= i;
498}
499#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */
500# define display_cpus(scr_width, scrbuf, lines_rem) ((void)0)
501#endif
502
[3621]503enum {
504 MI_MEMTOTAL,
505 MI_MEMFREE,
506 MI_MEMSHARED,
507 MI_SHMEM,
508 MI_BUFFERS,
509 MI_CACHED,
510 MI_SWAPTOTAL,
511 MI_SWAPFREE,
512 MI_DIRTY,
513 MI_WRITEBACK,
514 MI_ANONPAGES,
515 MI_MAPPED,
516 MI_SLAB,
517 MI_MAX
518};
519
520static void parse_meminfo(unsigned long meminfo[MI_MAX])
[2725]521{
[3621]522 static const char fields[] ALIGN1 =
523 "MemTotal\0"
524 "MemFree\0"
525 "MemShared\0"
526 "Shmem\0"
527 "Buffers\0"
528 "Cached\0"
529 "SwapTotal\0"
530 "SwapFree\0"
531 "Dirty\0"
532 "Writeback\0"
533 "AnonPages\0"
534 "Mapped\0"
535 "Slab\0";
536 char buf[60]; /* actual lines we expect are ~30 chars or less */
537 FILE *f;
538 int i;
[821]539
[3621]540 memset(meminfo, 0, sizeof(meminfo[0]) * MI_MAX);
541 f = xfopen_for_read("meminfo");
542 while (fgets(buf, sizeof(buf), f) != NULL) {
543 char *c = strchr(buf, ':');
544 if (!c)
545 continue;
546 *c = '\0';
547 i = index_in_strings(fields, buf);
548 if (i >= 0)
549 meminfo[i] = strtoul(c+1, NULL, 10);
550 }
551 fclose(f);
552}
[821]553
[3621]554static unsigned long display_header(int scr_width, int *lines_rem_p)
555{
556 char scrbuf[100]; /* [80] was a bit too low on 8Gb ram box */
557 char *buf;
558 unsigned long meminfo[MI_MAX];
[821]559
[3621]560 parse_meminfo(meminfo);
[821]561
[3621]562 /* Output memory info */
[2725]563 if (scr_width > (int)sizeof(scrbuf))
[821]564 scr_width = sizeof(scrbuf);
565 snprintf(scrbuf, scr_width,
[1765]566 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
[3621]567 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE],
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 */
[2725]573 printf(OPT_BATCH_MODE ? "%s\n" : "\033[H\033[J%s\n", scrbuf);
574 (*lines_rem_p)--;
[821]575
[3621]576 /* Display CPU time split as percentage of total time.
577 * This displays either a cumulative line or one line per CPU.
[1765]578 */
[2725]579 display_cpus(scr_width, scrbuf, lines_rem_p);
[1765]580
[3621]581 /* Read load average as a string */
582 buf = stpcpy(scrbuf, "Load average: ");
583 open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: "));
584 scrbuf[scr_width - 1] = '\0';
585 strchrnul(buf, '\n')[0] = '\0';
[1765]586 puts(scrbuf);
[2725]587 (*lines_rem_p)--;
[1765]588
[3621]589 return meminfo[MI_MEMTOTAL];
[821]590}
591
[2725]592static NOINLINE void display_process_list(int lines_rem, int scr_width)
[821]593{
594 enum {
[2725]595 BITS_PER_INT = sizeof(int) * 8
[821]596 };
597
[2725]598 top_status_t *s;
[1765]599 char vsz_str_buf[8];
[2725]600 unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */
[1765]601 /* xxx_shift and xxx_scale variables allow us to replace
602 * expensive divides with multiply and shift */
603 unsigned pmem_shift, pmem_scale, pmem_half;
604#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
[2725]605 unsigned tmp_unsigned;
[1765]606 unsigned pcpu_shift, pcpu_scale, pcpu_half;
607 unsigned busy_jifs;
[2725]608#endif
[821]609
610 /* what info of the processes is shown */
[2725]611 printf(OPT_BATCH_MODE ? "%.*s" : "\033[7m%.*s\033[0m", scr_width,
[3232]612 " PID PPID USER STAT VSZ %VSZ"
[2725]613 IF_FEATURE_TOP_SMP_PROCESS(" CPU")
614 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
615 " COMMAND");
616 lines_rem--;
[1765]617
618#if ENABLE_FEATURE_TOP_DECIMALS
[2725]619# define UPSCALE 1000
620# define CALC_STAT(name, val) div_t name = div((val), 10)
621# define SHOW_STAT(name) name.quot, '0'+name.rem
622# define FMT "%3u.%c"
[1765]623#else
[2725]624# define UPSCALE 100
625# define CALC_STAT(name, val) unsigned name = (val)
626# define SHOW_STAT(name) name
627# define FMT "%4u%%"
[1765]628#endif
[821]629 /*
[3232]630 * %VSZ = s->vsz/MemTotal
[821]631 */
[1765]632 pmem_shift = BITS_PER_INT-11;
633 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
634 /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */
[821]635 while (pmem_scale >= 512) {
636 pmem_scale /= 4;
637 pmem_shift -= 2;
638 }
[3232]639 pmem_half = (1U << pmem_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
[1765]640#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
[2725]641 busy_jifs = cur_jif.busy - prev_jif.busy;
[1765]642 /* This happens if there were lots of short-lived processes
643 * between two top updates (e.g. compilation) */
644 if (total_pcpu < busy_jifs) total_pcpu = busy_jifs;
645
[821]646 /*
647 * CPU% = s->pcpu/sum(s->pcpu) * busy_cpu_ticks/total_cpu_ticks
648 * (pcpu is delta of sys+user time between samples)
649 */
[2725]650 /* (cur_jif.xxx - prev_jif.xxx) and s->pcpu are
[821]651 * in 0..~64000 range (HZ*update_interval).
652 * we assume that unsigned is at least 32-bit.
653 */
654 pcpu_shift = 6;
[2725]655 pcpu_scale = UPSCALE*64 * (uint16_t)busy_jifs;
656 if (pcpu_scale == 0)
657 pcpu_scale = 1;
658 while (pcpu_scale < (1U << (BITS_PER_INT-2))) {
[821]659 pcpu_scale *= 4;
660 pcpu_shift += 2;
661 }
[2725]662 tmp_unsigned = (uint16_t)(cur_jif.total - prev_jif.total) * total_pcpu;
663 if (tmp_unsigned != 0)
664 pcpu_scale /= tmp_unsigned;
[821]665 /* we want (s->pcpu * pcpu_scale) to never overflow */
666 while (pcpu_scale >= 1024) {
667 pcpu_scale /= 4;
668 pcpu_shift -= 2;
669 }
[3232]670 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
[821]671 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
672#endif
673
[2725]674 /* Ok, all preliminary data is ready, go through the list */
675 scr_width += 2; /* account for leading '\n' and trailing NUL */
[3232]676 if (lines_rem > ntop - G_scroll_ofs)
677 lines_rem = ntop - G_scroll_ofs;
678 s = top + G_scroll_ofs;
[2725]679 while (--lines_rem >= 0) {
680 unsigned col;
[1765]681 CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
682#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
683 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
684#endif
[821]685
[2725]686 if (s->vsz >= 100000)
687 sprintf(vsz_str_buf, "%6ldm", s->vsz/1024);
[821]688 else
[3621]689 sprintf(vsz_str_buf, "%7lu", s->vsz);
[3232]690 /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */
[2725]691 col = snprintf(line_buf, scr_width,
692 "\n" "%5u%6u %-8.8s %s%s" FMT
693 IF_FEATURE_TOP_SMP_PROCESS(" %3d")
694 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
[1765]695 " ",
696 s->pid, s->ppid, get_cached_username(s->uid),
697 s->state, vsz_str_buf,
698 SHOW_STAT(pmem)
[2725]699 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
700 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
[1765]701 );
[2725]702 if ((int)(col + 1) < scr_width)
703 read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm);
704 fputs(line_buf, stdout);
[821]705 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
[2725]706 cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */
[821]707 s++;
708 }
709 /* printf(" %d", hist_iterations); */
[2725]710 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
711 fflush_all();
[821]712}
[1765]713#undef UPSCALE
714#undef SHOW_STAT
715#undef CALC_STAT
716#undef FMT
[821]717
718static void clearmems(void)
719{
[1765]720 clear_username_cache();
[821]721 free(top);
[2725]722 top = NULL;
[821]723}
724
[1765]725#if ENABLE_FEATURE_USE_TERMIOS
[821]726
727static void reset_term(void)
728{
[3232]729 if (!OPT_BATCH_MODE)
730 tcsetattr_stdin_TCSANOW(&initial_settings);
[2725]731 if (ENABLE_FEATURE_CLEAN_UP) {
732 clearmems();
733# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
734 free(prev_hist);
735# endif
736 }
[821]737}
738
[3232]739static void sig_catcher(int sig)
[821]740{
741 reset_term();
[3232]742 kill_myself_with_sig(sig);
[821]743}
[2725]744
[1765]745#endif /* FEATURE_USE_TERMIOS */
[821]746
[2725]747/*
748 * TOPMEM support
749 */
[821]750
[2725]751typedef unsigned long mem_t;
752
753typedef struct topmem_status_t {
754 unsigned pid;
755 char comm[COMM_LEN];
756 /* vsz doesn't count /dev/xxx mappings except /dev/zero */
757 mem_t vsz ;
758 mem_t vszrw ;
759 mem_t rss ;
760 mem_t rss_sh ;
761 mem_t dirty ;
762 mem_t dirty_sh;
763 mem_t stack ;
764} topmem_status_t;
765
766enum { NUM_SORT_FIELD = 7 };
767
768#define topmem ((topmem_status_t*)top)
769
770#if ENABLE_FEATURE_TOPMEM
771
772static int topmem_sort(char *a, char *b)
[821]773{
[2725]774 int n;
775 mem_t l, r;
776
777 n = offsetof(topmem_status_t, vsz) + (sort_field * sizeof(mem_t));
778 l = *(mem_t*)(a + n);
779 r = *(mem_t*)(b + n);
780 if (l == r) {
781 l = ((topmem_status_t*)a)->dirty;
782 r = ((topmem_status_t*)b)->dirty;
783 }
784 /* We want to avoid unsigned->signed and truncation errors */
785 /* l>r: -1, l=r: 0, l<r: 1 */
786 n = (l > r) ? -1 : (l != r);
787 return inverted ? -n : n;
788}
789
790/* display header info (meminfo / loadavg) */
791static void display_topmem_header(int scr_width, int *lines_rem_p)
792{
[3621]793 unsigned long meminfo[MI_MAX];
[2725]794
[3621]795 parse_meminfo(meminfo);
[2725]796
797 snprintf(line_buf, LINE_BUF_SIZE,
[3621]798 "Mem total:%lu anon:%lu map:%lu free:%lu",
799 meminfo[MI_MEMTOTAL],
800 meminfo[MI_ANONPAGES],
801 meminfo[MI_MAPPED],
802 meminfo[MI_MEMFREE]);
[2725]803 printf(OPT_BATCH_MODE ? "%.*s\n" : "\033[H\033[J%.*s\n", scr_width, line_buf);
804
805 snprintf(line_buf, LINE_BUF_SIZE,
[3621]806 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu",
807 meminfo[MI_SLAB],
808 meminfo[MI_BUFFERS],
809 meminfo[MI_CACHED],
810 meminfo[MI_DIRTY],
811 meminfo[MI_WRITEBACK]);
[2725]812 printf("%.*s\n", scr_width, line_buf);
813
814 snprintf(line_buf, LINE_BUF_SIZE,
[3621]815 "Swap total:%lu free:%lu", // TODO: % used?
816 meminfo[MI_SWAPTOTAL],
817 meminfo[MI_SWAPFREE]);
[2725]818 printf("%.*s\n", scr_width, line_buf);
819
820 (*lines_rem_p) -= 3;
821}
822
823static void ulltoa6_and_space(unsigned long long ul, char buf[6])
824{
825 /* see http://en.wikipedia.org/wiki/Tera */
[3621]826 smart_ulltoa5(ul, buf, " mgtpezy")[0] = ' ';
[2725]827}
828
829static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width)
830{
831#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK"
832#define MIN_WIDTH sizeof(HDR_STR)
[3232]833 const topmem_status_t *s = topmem + G_scroll_ofs;
[3621]834 char *cp, ch;
[2725]835
836 display_topmem_header(scr_width, &lines_rem);
[3621]837
[2725]838 strcpy(line_buf, HDR_STR " COMMAND");
[3621]839 /* Mark the ^FIELD^ we sort by */
840 cp = &line_buf[5 + sort_field * 6];
841 ch = "^_"[inverted];
842 cp[6] = ch;
843 do *cp++ = ch; while (*cp == ' ');
844
[2725]845 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf);
846 lines_rem--;
847
[3232]848 if (lines_rem > ntop - G_scroll_ofs)
849 lines_rem = ntop - G_scroll_ofs;
[2725]850 while (--lines_rem >= 0) {
851 /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
852 ulltoa6_and_space(s->pid , &line_buf[0*6]);
853 ulltoa6_and_space(s->vsz , &line_buf[1*6]);
854 ulltoa6_and_space(s->vszrw , &line_buf[2*6]);
855 ulltoa6_and_space(s->rss , &line_buf[3*6]);
856 ulltoa6_and_space(s->rss_sh , &line_buf[4*6]);
857 ulltoa6_and_space(s->dirty , &line_buf[5*6]);
858 ulltoa6_and_space(s->dirty_sh, &line_buf[6*6]);
859 ulltoa6_and_space(s->stack , &line_buf[7*6]);
860 line_buf[8*6] = '\0';
861 if (scr_width > (int)MIN_WIDTH) {
862 read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
863 }
864 printf("\n""%.*s", scr_width, line_buf);
865 s++;
866 }
867 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
868 fflush_all();
869#undef HDR_STR
870#undef MIN_WIDTH
871}
872
873#else
874void display_topmem_process_list(int lines_rem, int scr_width);
875int topmem_sort(char *a, char *b);
876#endif /* TOPMEM */
877
878/*
879 * end TOPMEM support
880 */
881
882enum {
883 TOP_MASK = 0
884 | PSSCAN_PID
885 | PSSCAN_PPID
886 | PSSCAN_VSZ
887 | PSSCAN_STIME
888 | PSSCAN_UTIME
889 | PSSCAN_STATE
890 | PSSCAN_COMM
891 | PSSCAN_CPU
892 | PSSCAN_UIDGID,
893 TOPMEM_MASK = 0
894 | PSSCAN_PID
895 | PSSCAN_SMAPS
896 | PSSCAN_COMM,
[3232]897 EXIT_MASK = (unsigned)-1,
[2725]898};
899
[3232]900#if ENABLE_FEATURE_USE_TERMIOS
901static unsigned handle_input(unsigned scan_mask, unsigned interval)
902{
903 if (option_mask32 & OPT_EOF) {
904 /* EOF on stdin ("top </dev/null") */
905 sleep(interval);
906 return scan_mask;
907 }
908
909 while (1) {
910 int32_t c;
911
912 c = read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
913 if (c == -1 && errno != EAGAIN) {
914 /* error/EOF */
915 option_mask32 |= OPT_EOF;
916 break;
917 }
918 interval = 0;
919
920 if (c == initial_settings.c_cc[VINTR])
921 return EXIT_MASK;
922 if (c == initial_settings.c_cc[VEOF])
923 return EXIT_MASK;
924
925 if (c == KEYCODE_UP) {
926 G_scroll_ofs--;
927 goto normalize_ofs;
928 }
929 if (c == KEYCODE_DOWN) {
930 G_scroll_ofs++;
931 goto normalize_ofs;
932 }
933 if (c == KEYCODE_HOME) {
934 G_scroll_ofs = 0;
935 break;
936 }
937 if (c == KEYCODE_END) {
938 G_scroll_ofs = ntop - G.lines / 2;
939 goto normalize_ofs;
940 }
941 if (c == KEYCODE_PAGEUP) {
942 G_scroll_ofs -= G.lines / 2;
943 goto normalize_ofs;
944 }
945 if (c == KEYCODE_PAGEDOWN) {
946 G_scroll_ofs += G.lines / 2;
947 normalize_ofs:
948 if (G_scroll_ofs >= ntop)
949 G_scroll_ofs = ntop - 1;
950 if (G_scroll_ofs < 0)
951 G_scroll_ofs = 0;
952 break;
953 }
954
955 c |= 0x20; /* lowercase */
956 if (c == 'q')
957 return EXIT_MASK;
958
959 if (c == 'n') {
960 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
961 sort_function[0] = pid_sort;
962 continue;
963 }
964 if (c == 'm') {
965 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
966 sort_function[0] = mem_sort;
967# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
968 sort_function[1] = pcpu_sort;
969 sort_function[2] = time_sort;
970# endif
971 continue;
972 }
973# if ENABLE_FEATURE_SHOW_THREADS
974 if (c == 'h'
975 IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
976 ) {
977 scan_mask ^= PSSCAN_TASKS;
978 continue;
979 }
980# endif
981# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
982 if (c == 'p') {
983 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
984 sort_function[0] = pcpu_sort;
985 sort_function[1] = mem_sort;
986 sort_function[2] = time_sort;
987 continue;
988 }
989 if (c == 't') {
990 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
991 sort_function[0] = time_sort;
992 sort_function[1] = mem_sort;
993 sort_function[2] = pcpu_sort;
994 continue;
995 }
996# if ENABLE_FEATURE_TOPMEM
997 if (c == 's') {
998 scan_mask = TOPMEM_MASK;
999 free(prev_hist);
1000 prev_hist = NULL;
1001 prev_hist_count = 0;
1002 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1003 continue;
1004 }
1005# endif
1006 if (c == 'r') {
1007 inverted ^= 1;
1008 continue;
1009 }
1010# if ENABLE_FEATURE_TOP_SMP_CPU
1011 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
1012 if (c == 'c' || c == '1') {
1013 /* User wants to toggle per cpu <> aggregate */
1014 if (smp_cpu_info) {
1015 free(cpu_prev_jif);
1016 free(cpu_jif);
1017 cpu_jif = &cur_jif;
1018 cpu_prev_jif = &prev_jif;
1019 } else {
1020 /* Prepare for xrealloc() */
1021 cpu_jif = cpu_prev_jif = NULL;
1022 }
1023 num_cpus = 0;
1024 smp_cpu_info = !smp_cpu_info;
1025 get_jiffy_counts();
1026 continue;
1027 }
1028# endif
1029# endif
1030 break; /* unknown key -> force refresh */
1031 }
1032
1033 return scan_mask;
1034}
1035#endif
1036
1037//usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU
1038//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__
1039//usage:#else
1040//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...)
1041//usage:#endif
1042//usage:#define top_trivial_usage
1043//usage: "[-b] [-nCOUNT] [-dSECONDS]" IF_FEATURE_TOPMEM(" [-m]")
1044//usage:#define top_full_usage "\n\n"
1045//usage: "Provide a view of process activity in real time."
1046//usage: "\n""Read the status of all processes from /proc each SECONDS"
1047//usage: "\n""and display a screenful of them."
1048//usage: "\n""Keys:"
1049//usage: "\n"" N/M"
1050//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/P")
1051//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/T")
1052//usage: ": " IF_FEATURE_TOPMEM("show CPU usage, ") "sort by pid/mem"
1053//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu")
1054//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time")
1055//usage: IF_FEATURE_TOPMEM(
1056//usage: "\n"" S: show memory"
1057//usage: )
1058//usage: "\n"" R: reverse sort"
1059//usage: IF_SHOW_THREADS_OR_TOP_SMP(
1060//usage: "\n"" "
1061//usage: IF_FEATURE_SHOW_THREADS("H: toggle threads")
1062//usage: IF_FEATURE_SHOW_THREADS(IF_FEATURE_TOP_SMP_CPU(", "))
1063//usage: IF_FEATURE_TOP_SMP_CPU("1: toggle SMP")
1064//usage: )
1065//usage: "\n"" Q,^C: exit"
1066//usage: "\n"
1067//usage: "\n""Options:"
1068//usage: "\n"" -b Batch mode"
1069//usage: "\n"" -n N Exit after N iterations"
1070//usage: "\n"" -d N Delay between updates"
1071//usage: IF_FEATURE_TOPMEM(
1072//usage: "\n"" -m Same as 's' key"
1073//usage: )
1074
1075/* Interactive testing:
1076 * echo sss | ./busybox top
1077 * - shows memory screen
1078 * echo sss | ./busybox top -bn1 >mem
1079 * - saves memory screen - the *whole* list, not first NROWS processes!
1080 * echo .m.s.s.s.s.s.s.q | ./busybox top -b >z
1081 * - saves several different screens, and exits
1082 *
1083 * TODO: -i STRING param as a better alternative?
1084 */
1085
[2725]1086int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1087int top_main(int argc UNUSED_PARAM, char **argv)
1088{
1089 int iterations;
[3232]1090 unsigned col;
[2725]1091 unsigned interval;
1092 char *str_interval, *str_iterations;
1093 unsigned scan_mask = TOP_MASK;
[1765]1094#if ENABLE_FEATURE_USE_TERMIOS
[821]1095 struct termios new_settings;
[2725]1096#endif
[1765]1097
[2725]1098 INIT_G();
[821]1099
[2725]1100 interval = 5; /* default update interval is 5 seconds */
1101 iterations = 0; /* infinite */
1102#if ENABLE_FEATURE_TOP_SMP_CPU
1103 /*num_cpus = 0;*/
1104 /*smp_cpu_info = 0;*/ /* to start with show aggregate */
1105 cpu_jif = &cur_jif;
1106 cpu_prev_jif = &prev_jif;
1107#endif
1108
1109 /* all args are options; -n NUM */
1110 opt_complementary = "-"; /* options can be specified w/o dash */
1111 col = getopt32(argv, "d:n:b"IF_FEATURE_TOPMEM("m"), &str_interval, &str_iterations);
1112#if ENABLE_FEATURE_TOPMEM
1113 if (col & OPT_m) /* -m (busybox specific) */
1114 scan_mask = TOPMEM_MASK;
1115#endif
1116 if (col & OPT_d) {
1117 /* work around for "-d 1" -> "-d -1" done by getopt32
1118 * (opt_complementary == "-" does this) */
1119 if (str_interval[0] == '-')
1120 str_interval++;
1121 /* Need to limit it to not overflow poll timeout */
1122 interval = xatou16(str_interval);
1123 }
1124 if (col & OPT_n) {
1125 if (str_iterations[0] == '-')
1126 str_iterations++;
1127 iterations = xatou(str_iterations);
1128 }
1129
[821]1130 /* change to /proc */
[1765]1131 xchdir("/proc");
[821]1132
[1765]1133#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
[821]1134 sort_function[0] = pcpu_sort;
1135 sort_function[1] = mem_sort;
1136 sort_function[2] = time_sort;
1137#else
[2725]1138 sort_function[0] = mem_sort;
1139#endif
[821]1140
[3232]1141 if (OPT_BATCH_MODE) {
1142 option_mask32 |= OPT_EOF;
1143 }
1144#if ENABLE_FEATURE_USE_TERMIOS
1145 else {
1146 tcgetattr(0, (void *) &initial_settings);
1147 memcpy(&new_settings, &initial_settings, sizeof(new_settings));
1148 /* unbuffered input, turn off echo */
1149 new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
1150 tcsetattr_stdin_TCSANOW(&new_settings);
1151 }
1152
1153 bb_signals(BB_FATAL_SIGS, sig_catcher);
1154
1155 /* Eat initial input, if any */
1156 scan_mask = handle_input(scan_mask, 0);
1157#endif
1158
1159 while (scan_mask != EXIT_MASK) {
[1765]1160 procps_status_t *p = NULL;
[821]1161
[3232]1162 if (OPT_BATCH_MODE) {
1163 G.lines = INT_MAX;
1164 col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
1165 } else {
1166 G.lines = 24; /* default */
1167 col = 79;
[1765]1168#if ENABLE_FEATURE_USE_TERMIOS
[3232]1169 /* We output to stdout, we need size of stdout (not stdin)! */
1170 get_terminal_width_height(STDOUT_FILENO, &col, &G.lines);
1171 if (G.lines < 5 || col < 10) {
1172 sleep(interval);
1173 continue;
1174 }
1175#endif
1176 if (col > LINE_BUF_SIZE - 2)
1177 col = LINE_BUF_SIZE - 2;
[821]1178 }
1179
1180 /* read process IDs & status for all the processes */
[3232]1181 ntop = 0;
[2725]1182 while ((p = procps_scan(p, scan_mask)) != NULL) {
1183 int n;
[3621]1184
1185 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
[2725]1186 n = ntop;
1187 top = xrealloc_vector(top, 6, ntop++);
1188 top[n].pid = p->pid;
1189 top[n].ppid = p->ppid;
1190 top[n].vsz = p->vsz;
[1765]1191#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
[2725]1192 top[n].ticks = p->stime + p->utime;
[1765]1193#endif
[2725]1194 top[n].uid = p->uid;
1195 strcpy(top[n].state, p->state);
1196 strcpy(top[n].comm, p->comm);
1197#if ENABLE_FEATURE_TOP_SMP_PROCESS
1198 top[n].last_seen_on_cpu = p->last_seen_on_cpu;
1199#endif
1200 }
1201#if ENABLE_FEATURE_TOPMEM
1202 else { /* TOPMEM */
1203 if (!(p->smaps.mapped_ro | p->smaps.mapped_rw))
1204 continue; /* kernel threads are ignored */
1205 n = ntop;
1206 /* No bug here - top and topmem are the same */
1207 top = xrealloc_vector(topmem, 6, ntop++);
1208 strcpy(topmem[n].comm, p->comm);
1209 topmem[n].pid = p->pid;
1210 topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro;
1211 topmem[n].vszrw = p->smaps.mapped_rw;
1212 topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty;
1213 topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh;
1214 topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty;
1215 topmem[n].dirty_sh = p->smaps.shared_dirty;
1216 topmem[n].stack = p->smaps.stack;
1217 }
1218#endif
1219 } /* end of "while we read /proc" */
[821]1220 if (ntop == 0) {
[2725]1221 bb_error_msg("no process info in /proc");
1222 break;
[821]1223 }
[2725]1224
[3621]1225 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
[1765]1226#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
[2725]1227 if (!prev_hist_count) {
1228 do_stats();
1229 usleep(100000);
1230 clearmems();
1231 continue;
1232 }
[821]1233 do_stats();
[2725]1234 /* TODO: we don't need to sort all 10000 processes, we need to find top 24! */
1235 qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
1236#else
1237 qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
1238#endif
[3621]1239 display_process_list(G.lines, col);
[821]1240 }
[2725]1241#if ENABLE_FEATURE_TOPMEM
1242 else { /* TOPMEM */
1243 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
[3621]1244 display_topmem_process_list(G.lines, col);
[2725]1245 }
1246#endif
1247 clearmems();
1248 if (iterations >= 0 && !--iterations)
1249 break;
1250#if !ENABLE_FEATURE_USE_TERMIOS
1251 sleep(interval);
[821]1252#else
[3232]1253 scan_mask = handle_input(scan_mask, interval);
[3621]1254#endif
[3232]1255 } /* end of "while (not Q)" */
[2725]1256
1257 bb_putchar('\n');
1258#if ENABLE_FEATURE_USE_TERMIOS
1259 reset_term();
1260#endif
[821]1261 return EXIT_SUCCESS;
1262}
Note: See TracBrowser for help on using the repository browser.