Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/procps/top.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 815 by niro, Sat Sep 1 22:45:15 2007 UTC revision 816 by niro, Fri Apr 24 18:33:46 2009 UTC
# Line 16  Line 16 
16   * (C) Eero Tamminen <oak at welho dot com>   * (C) Eero Tamminen <oak at welho dot com>
17   *   *
18   * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>   * 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
25   */   */
26    
27  /* Original code Copyrights */  /* Original code Copyrights */
# Line 28  Line 34 
34   * GNU Library General Public License   * GNU Library General Public License
35   */   */
36    
37  #include "busybox.h"  #include "libbb.h"
38    
39    
40  typedef struct {  typedef struct top_status_t {
41   unsigned long rss;   unsigned long vsz;
42  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
43   unsigned long ticks;   unsigned long ticks;
44   unsigned pcpu; /* delta of ticks */   unsigned pcpu; /* delta of ticks */
# Line 41  typedef struct { Line 47  typedef struct {
47   unsigned uid;   unsigned uid;
48   char state[4];   char state[4];
49   char comm[COMM_LEN];   char comm[COMM_LEN];
50    #if ENABLE_FEATURE_TOP_SMP_PROCESS
51     int last_seen_on_cpu;
52    #endif
53  } top_status_t;  } top_status_t;
54  static top_status_t *top;  
55  static int ntop;  typedef struct jiffy_counts_t {
56     unsigned long long usr,nic,sys,idle,iowait,irq,softirq,steal;
57     unsigned long long total;
58     unsigned long long busy;
59    } jiffy_counts_t;
60    
61  /* This structure stores some critical information from one frame to  /* This structure stores some critical information from one frame to
62     the next. Used for finding deltas. */     the next. Used for finding deltas. */
63  struct save_hist {  typedef struct save_hist {
64   unsigned long ticks;   unsigned long ticks;
65   unsigned pid;   pid_t pid;
66  };  } save_hist;
67  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE  
68  static struct save_hist *prev_hist;  typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
69  static int prev_hist_count;  
70  /* static int hist_iterations; */  
71  static unsigned total_pcpu;  enum { SORT_DEPTH = 3 };
72  /* static unsigned long total_rss; */  
73    
74    struct globals {
75     top_status_t *top;
76     int ntop;
77    #if ENABLE_FEATURE_TOPMEM
78     smallint sort_field;
79     smallint inverted;
80  #endif  #endif
81    #if ENABLE_FEATURE_TOP_SMP_CPU
82     smallint smp_cpu_info; /* one/many cpu info lines? */
83    #endif
84    #if ENABLE_FEATURE_USE_TERMIOS
85     struct termios initial_settings;
86    #endif
87    #if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
88     cmp_funcp sort_function[1];
89    #else
90     cmp_funcp sort_function[SORT_DEPTH];
91     struct save_hist *prev_hist;
92     int prev_hist_count;
93     jiffy_counts_t cur_jif, prev_jif;
94     /* int hist_iterations; */
95     unsigned total_pcpu;
96     /* unsigned long total_vsz; */
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
103     char line_buf[80];
104    };
105    
106    enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) };
107    
108    #define G (*(struct globals*)&bb_common_bufsiz1)
109    #define INIT_G() do { \
110     struct G_sizecheck { \
111     char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
112     }; \
113    } while (0)
114    #define top              (G.top               )
115    #define ntop             (G.ntop              )
116    #define sort_field       (G.sort_field        )
117    #define inverted         (G.inverted          )
118    #define smp_cpu_info     (G.smp_cpu_info      )
119    #define initial_settings (G.initial_settings  )
120    #define sort_function    (G.sort_function     )
121    #define prev_hist        (G.prev_hist         )
122    #define prev_hist_count  (G.prev_hist_count   )
123    #define cur_jif          (G.cur_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          )
128    #define total_pcpu       (G.total_pcpu        )
129    #define line_buf         (G.line_buf          )
130    
131    enum {
132     OPT_d = (1 << 0),
133     OPT_n = (1 << 1),
134     OPT_b = (1 << 2),
135     OPT_EOF = (1 << 3), /* pseudo: "we saw EOF in stdin" */
136    };
137    #define OPT_BATCH_MODE (option_mask32 & OPT_b)
138    
 #define OPT_BATCH_MODE (option_mask32 & 0x4)  
139    
140  #if ENABLE_FEATURE_USE_TERMIOS  #if ENABLE_FEATURE_USE_TERMIOS
141  static int pid_sort(top_status_t *P, top_status_t *Q)  static int pid_sort(top_status_t *P, top_status_t *Q)
# Line 72  static int pid_sort(top_status_t *P, top Line 149  static int pid_sort(top_status_t *P, top
149  static int mem_sort(top_status_t *P, top_status_t *Q)  static int mem_sort(top_status_t *P, top_status_t *Q)
150  {  {
151   /* We want to avoid unsigned->signed and truncation errors */   /* We want to avoid unsigned->signed and truncation errors */
152   if (Q->rss < P->rss) return -1;   if (Q->vsz < P->vsz) return -1;
153   return Q->rss != P->rss; /* 0 if ==, 1 if > */   return Q->vsz != P->vsz; /* 0 if ==, 1 if > */
154  }  }
155    
156    
157  typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
   
 #if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE  
   
 static cmp_funcp sort_function;  
   
 #else  
   
 enum { SORT_DEPTH = 3 };  
   
 static cmp_funcp sort_function[SORT_DEPTH];  
158    
159  static int pcpu_sort(top_status_t *P, top_status_t *Q)  static int pcpu_sort(top_status_t *P, top_status_t *Q)
160  {  {
# Line 103  static int time_sort(top_status_t *P, to Line 170  static int time_sort(top_status_t *P, to
170   return Q->ticks != P->ticks; /* 0 if ==, 1 if > */   return Q->ticks != P->ticks; /* 0 if ==, 1 if > */
171  }  }
172    
173  static int mult_lvl_cmp(void* a, void* b) {  static int mult_lvl_cmp(void* a, void* b)
174    {
175   int i, cmp_val;   int i, cmp_val;
176    
177   for (i = 0; i < SORT_DEPTH; i++) {   for (i = 0; i < SORT_DEPTH; i++) {
# Line 114  static int mult_lvl_cmp(void* a, void* b Line 182  static int mult_lvl_cmp(void* a, void* b
182   return 0;   return 0;
183  }  }
184    
185    /* NOINLINE so that complier doesn't unfold the call
186     * causing multiple copies of the arithmatic instrns
187     */
188    static 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    }
212    
 typedef struct {  
  unsigned long long usr,nic,sys,idle,iowait,irq,softirq,steal;  
  unsigned long long total;  
  unsigned long long busy;  
 } jiffy_counts_t;  
 static jiffy_counts_t jif, prev_jif;  
213  static void get_jiffy_counts(void)  static void get_jiffy_counts(void)
214  {  {
215   FILE* fp = xfopen("stat", "r");   FILE* fp = xfopen_for_read("stat");
216   prev_jif = jif;  
217   if (fscanf(fp, "cpu  %lld %lld %lld %lld %lld %lld %lld %lld",   /* We need to parse cumulative counts even if SMP CPU display is on,
218   &jif.usr,&jif.nic,&jif.sys,&jif.idle,   * they are used to calculate per process CPU% */
219   &jif.iowait,&jif.irq,&jif.softirq,&jif.steal) < 4) {   prev_jif = cur_jif;
220   bb_error_msg_and_die("failed to read /proc/stat");   if (read_cpu_jiffy(fp, &cur_jif) < 4)
221     bb_error_msg_and_die("can't read /proc/stat");
222    
223    #if !ENABLE_FEATURE_TOP_SMP_CPU
224     fclose(fp);
225     return;
226    #else
227     if (!smp_cpu_info) {
228     fclose(fp);
229     return;
230     }
231    
232     if (!num_cpus) {
233     /* First time here. How many CPUs?
234     * There will be at least 1 /proc/stat line with cpu%d
235     */
236     while (1) {
237     cpu_jif = xrealloc_vector(cpu_jif, 1, num_cpus);
238     if (read_cpu_jiffy(fp, &cpu_jif[num_cpus]) <= 4)
239     break;
240     num_cpus++;
241     }
242     if (num_cpus == 0) /* /proc/stat with only "cpu ..." line?! */
243     smp_cpu_info = 0;
244    
245     cpu_prev_jif = xzalloc(sizeof(cpu_prev_jif[0]) * num_cpus);
246    
247     /* Otherwise the first per cpu display shows all 100% idles */
248     usleep(50000);
249     } else { /* Non first time invocation */
250     jiffy_counts_t *tmp;
251     int i;
252    
253     /* First switch the sample pointers: no need to copy */
254     tmp = cpu_prev_jif;
255     cpu_prev_jif = cpu_jif;
256     cpu_jif = tmp;
257    
258     /* Get the new samples */
259     for (i = 0; i < num_cpus; i++)
260     read_cpu_jiffy(fp, &cpu_jif[i]);
261   }   }
262    #endif
263   fclose(fp);   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;  
264  }  }
265    
   
266  static void do_stats(void)  static void do_stats(void)
267  {  {
268   top_status_t *cur;   top_status_t *cur;
# Line 147  static void do_stats(void) Line 272  static void do_stats(void)
272    
273   get_jiffy_counts();   get_jiffy_counts();
274   total_pcpu = 0;   total_pcpu = 0;
275   /* total_rss = 0; */   /* total_vsz = 0; */
276   new_hist = xmalloc(sizeof(struct save_hist)*ntop);   new_hist = xmalloc(sizeof(new_hist[0]) * ntop);
277   /*   /*
278   * Make a pass through the data to get stats.   * Make a pass through the data to get stats.
279   */   */
# Line 179  static void do_stats(void) Line 304  static void do_stats(void)
304   i = (i+1) % prev_hist_count;   i = (i+1) % prev_hist_count;
305   /* hist_iterations++; */   /* hist_iterations++; */
306   } while (i != last_i);   } while (i != last_i);
307   /* total_rss += cur->rss; */   /* total_vsz += cur->vsz; */
308   }   }
309    
310   /*   /*
# Line 189  static void do_stats(void) Line 314  static void do_stats(void)
314   prev_hist = new_hist;   prev_hist = new_hist;
315   prev_hist_count = ntop;   prev_hist_count = ntop;
316  }  }
317    
318  #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */  #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
319    
320    #if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
321    /* formats 7 char string (8 with terminating NUL) */
322    static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
323    {
324     unsigned t;
325     if (value >= total) { /* 100% ? */
326     strcpy(pbuf, "  100% ");
327     return pbuf;
328     }
329     /* else generate " [N/space]N.N% " string */
330     value = 1000 * value / total;
331     t = value / 100;
332     value = value % 100;
333     pbuf[0] = ' ';
334     pbuf[1] = t ? t + '0' : ' ';
335     pbuf[2] = '0' + (value / 10);
336     pbuf[3] = '.';
337     pbuf[4] = '0' + (value % 10);
338     pbuf[5] = '%';
339     pbuf[6] = ' ';
340     pbuf[7] = '\0';
341     return pbuf;
342    }
343    #endif
344    
345    #if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
346    static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
347    {
348     /*
349     * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100%
350     */
351     unsigned total_diff;
352     jiffy_counts_t *p_jif, *p_prev_jif;
353     int i;
354    
355    #if ENABLE_FEATURE_TOP_SMP_CPU
356     int n_cpu_lines;
357    #endif
358    
359     /* using (unsigned) casts to make operations cheaper */
360    #define  CALC_TOT_DIFF  ((unsigned)(p_jif->total - p_prev_jif->total) ? : 1)
361    
362    #if ENABLE_FEATURE_TOP_DECIMALS
363    #define CALC_STAT(xxx) char xxx[8]
364    #define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(p_jif->xxx - p_prev_jif->xxx), total_diff)
365    #define FMT "%s"
366    #else
367    #define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(p_jif->xxx - p_prev_jif->xxx) / total_diff
368    #define SHOW_STAT(xxx) xxx
369    #define FMT "%4u%% "
370    #endif
371    
372    #if !ENABLE_FEATURE_TOP_SMP_CPU
373     {
374     i = 1;
375     p_jif = &cur_jif;
376     p_prev_jif = &prev_jif;
377    #else
378     /* Loop thru CPU(s) */
379     n_cpu_lines = smp_cpu_info ? num_cpus : 1;
380     if (n_cpu_lines > *lines_rem_p)
381     n_cpu_lines = *lines_rem_p;
382    
383     for (i = 0; i < n_cpu_lines; i++) {
384     p_jif = &cpu_jif[i];
385     p_prev_jif = &cpu_prev_jif[i];
386    #endif
387     total_diff = CALC_TOT_DIFF;
388    
389     { /* Need a block: CALC_STAT are declarations */
390     CALC_STAT(usr);
391     CALC_STAT(sys);
392     CALC_STAT(nic);
393     CALC_STAT(idle);
394     CALC_STAT(iowait);
395     CALC_STAT(irq);
396     CALC_STAT(softirq);
397     /*CALC_STAT(steal);*/
398    
399     snprintf(scrbuf, scr_width,
400     /* Barely fits in 79 chars when in "decimals" mode. */
401    #if ENABLE_FEATURE_TOP_SMP_CPU
402     "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
403     (smp_cpu_info ? utoa(i) : ""),
404    #else
405     "CPU:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
406    #endif
407     SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle),
408     SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq)
409     /*, SHOW_STAT(steal) - what is this 'steal' thing? */
410     /* I doubt anyone wants to know it */
411     );
412     puts(scrbuf);
413     }
414     }
415    #undef SHOW_STAT
416    #undef CALC_STAT
417    #undef FMT
418     *lines_rem_p -= i;
419    }
420    #else  /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */
421    #define display_cpus(scr_width, scrbuf, lines_rem) ((void)0)
422    #endif
423    
424  /* display generic info (meminfo / loadavg) */  static unsigned long display_header(int scr_width, int *lines_rem_p)
 static unsigned long display_generic(int scr_width)  
425  {  {
426   FILE *fp;   FILE *fp;
427   char buf[80];   char buf[80];
428   char scrbuf[80];   char scrbuf[80];
  char *end;  
429   unsigned long total, used, mfree, shared, buffers, cached;   unsigned long total, used, mfree, shared, buffers, cached;
  unsigned int needs_conversion = 1;  
430    
431   /* read memory info */   /* read memory info */
432   fp = xfopen("meminfo", "r");   fp = xfopen_for_read("meminfo");
433    
434   /*   /*
435   * Old kernels (such as 2.4.x) had a nice summary of memory info that   * Old kernels (such as 2.4.x) had a nice summary of memory info that
# Line 219  static unsigned long display_generic(int Line 445  static unsigned long display_generic(int
445   fgets(buf, sizeof(buf), fp);    /* skip first line */   fgets(buf, sizeof(buf), fp);    /* skip first line */
446    
447   fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",   fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
448     &total, &used, &mfree, &shared, &buffers, &cached);   &total, &used, &mfree, &shared, &buffers, &cached);
449     /* convert to kilobytes */
450     used /= 1024;
451     mfree /= 1024;
452     shared /= 1024;
453     buffers /= 1024;
454     cached /= 1024;
455     total /= 1024;
456   } else {   } else {
457   /*   /*
458   * Revert to manual parsing, which incidentally already has the   * Revert to manual parsing, which incidentally already has the
459   * sizes in kilobytes. This should be safe for both 2.4 and   * sizes in kilobytes. This should be safe for both 2.4 and
460   * 2.6.   * 2.6.
461   */   */
  needs_conversion = 0;  
   
462   fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);   fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);
463    
464   /*   /*
# Line 244  static unsigned long display_generic(int Line 475  static unsigned long display_generic(int
475   }   }
476   fclose(fp);   fclose(fp);
477    
478   /* read load average as a string */   /* output memory info */
479   buf[0] = '\0';   if (scr_width > (int)sizeof(scrbuf))
  open_read_close("loadavg", buf, sizeof(buf));  
  end = strchr(buf, ' ');  
  if (end) end = strchr(end+1, ' ');  
  if (end) end = strchr(end+1, ' ');  
  if (end) *end = '\0';  
   
  if (needs_conversion) {  
  /* convert to kilobytes */  
  used /= 1024;  
  mfree /= 1024;  
  shared /= 1024;  
  buffers /= 1024;  
  cached /= 1024;  
  total /= 1024;  
  }  
   
  /* output memory info and load average */  
  /* clear screen & go to top */  
  if (scr_width > sizeof(scrbuf))  
480   scr_width = sizeof(scrbuf);   scr_width = sizeof(scrbuf);
481   snprintf(scrbuf, scr_width,   snprintf(scrbuf, scr_width,
482   "Mem: %ldK used, %ldK free, %ldK shrd, %ldK buff, %ldK cached",   "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
483   used, mfree, shared, buffers, cached);   used, mfree, shared, buffers, cached);
484     /* clear screen & go to top */
485   printf(OPT_BATCH_MODE ? "%s\n" : "\e[H\e[J%s\n", scrbuf);   printf(OPT_BATCH_MODE ? "%s\n" : "\e[H\e[J%s\n", scrbuf);
486     (*lines_rem_p)--;
487    
488     /* Display CPU time split as percentage of total time
489     * This displays either a cumulative line or one line per CPU
490     */
491     display_cpus(scr_width, scrbuf, lines_rem_p);
492    
493     /* read load average as a string */
494     buf[0] = '\0';
495     open_read_close("loadavg", buf, sizeof(buf) - 1);
496     buf[sizeof(buf) - 1] = '\n';
497     *strchr(buf, '\n') = '\0';
498   snprintf(scrbuf, scr_width, "Load average: %s", buf);   snprintf(scrbuf, scr_width, "Load average: %s", buf);
499   printf("%s\n", scrbuf);   puts(scrbuf);
500     (*lines_rem_p)--;
501    
502   return total;   return total;
503  }  }
504    
505    static NOINLINE void display_process_list(int lines_rem, int scr_width)
 /* display process statuses */  
 static void display_status(int count, int scr_width)  
506  {  {
507   enum {   enum {
508   bits_per_int = sizeof(int)*8   BITS_PER_INT = sizeof(int) * 8
509   };   };
510    
511   top_status_t *s = top;   top_status_t *s;
512   char rss_str_buf[8];   char vsz_str_buf[8];
513   unsigned long total_memory = display_generic(scr_width); /* or use total_rss? */   unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */
514   unsigned pmem_shift, pmem_scale;   /* xxx_shift and xxx_scale variables allow us to replace
515     * expensive divides with multiply and shift */
516     unsigned pmem_shift, pmem_scale, pmem_half;
517  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
518   unsigned pcpu_shift, pcpu_scale;   unsigned pcpu_shift, pcpu_scale, pcpu_half;
519   unsigned busy_jifs;   unsigned busy_jifs;
520    #endif
521    
522   /* what info of the processes is shown */   /* what info of the processes is shown */
523   printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,   printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,
524   "  PID USER     STATUS   RSS  PPID %CPU %MEM COMMAND");   "  PID  PPID USER     STAT   VSZ %MEM"
525  #define MIN_WIDTH \  #if ENABLE_FEATURE_TOP_SMP_PROCESS
526   sizeof( "  PID USER     STATUS   RSS  PPID %CPU %MEM C")   " CPU"
527  #else  #endif
528   printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
529   "  PID USER     STATUS   RSS  PPID %MEM COMMAND");   " %CPU"
 #define MIN_WIDTH \  
  sizeof( "  PID USER     STATUS   RSS  PPID %MEM C")  
530  #endif  #endif
531     " COMMAND");
532     lines_rem--;
533    
534    #if ENABLE_FEATURE_TOP_DECIMALS
535    #define UPSCALE 1000
536    #define CALC_STAT(name, val) div_t name = div((val), 10)
537    #define SHOW_STAT(name) name.quot, '0'+name.rem
538    #define FMT "%3u.%c"
539    #else
540    #define UPSCALE 100
541    #define CALC_STAT(name, val) unsigned name = (val)
542    #define SHOW_STAT(name) name
543    #define FMT "%4u%%"
544    #endif
545   /*   /*
546   * MEM% = s->rss/MemTotal   * MEM% = s->vsz/MemTotal
547   */   */
548   pmem_shift = bits_per_int-11;   pmem_shift = BITS_PER_INT-11;
549   pmem_scale = 1000*(1U<<(bits_per_int-11)) / total_memory;   pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
550   /* s->rss is in kb. we want (s->rss * pmem_scale) to never overflow */   /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */
551   while (pmem_scale >= 512) {   while (pmem_scale >= 512) {
552   pmem_scale /= 4;   pmem_scale /= 4;
553   pmem_shift -= 2;   pmem_shift -= 2;
554   }   }
555     pmem_half = (1U << pmem_shift) / (ENABLE_FEATURE_TOP_DECIMALS? 20 : 2);
556  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
557   busy_jifs = jif.busy - prev_jif.busy;   busy_jifs = cur_jif.busy - prev_jif.busy;
558   /* This happens if there were lots of short-lived processes   /* This happens if there were lots of short-lived processes
559   * between two top updates (e.g. compilation) */   * between two top updates (e.g. compilation) */
560   if (total_pcpu < busy_jifs) total_pcpu = busy_jifs;   if (total_pcpu < busy_jifs) total_pcpu = busy_jifs;
# Line 327  static void display_status(int count, in Line 563  static void display_status(int count, in
563   * CPU% = s->pcpu/sum(s->pcpu) * busy_cpu_ticks/total_cpu_ticks   * CPU% = s->pcpu/sum(s->pcpu) * busy_cpu_ticks/total_cpu_ticks
564   * (pcpu is delta of sys+user time between samples)   * (pcpu is delta of sys+user time between samples)
565   */   */
566   /* (jif.xxx - prev_jif.xxx) and s->pcpu are   /* (cur_jif.xxx - prev_jif.xxx) and s->pcpu are
567   * in 0..~64000 range (HZ*update_interval).   * in 0..~64000 range (HZ*update_interval).
568   * we assume that unsigned is at least 32-bit.   * we assume that unsigned is at least 32-bit.
569   */   */
570   pcpu_shift = 6;   pcpu_shift = 6;
571   pcpu_scale = (1000*64*(uint16_t)busy_jifs ? : 1);   pcpu_scale = (UPSCALE*64 * (uint16_t)busy_jifs ? : 1);
572   while (pcpu_scale < (1U<<(bits_per_int-2))) {   while (pcpu_scale < (1U << (BITS_PER_INT-2))) {
573   pcpu_scale *= 4;   pcpu_scale *= 4;
574   pcpu_shift += 2;   pcpu_shift += 2;
575   }   }
576   pcpu_scale /= ( (uint16_t)(jif.total-prev_jif.total)*total_pcpu ? : 1);   pcpu_scale /= ( (uint16_t)(cur_jif.total - prev_jif.total) * total_pcpu ? : 1);
577   /* we want (s->pcpu * pcpu_scale) to never overflow */   /* we want (s->pcpu * pcpu_scale) to never overflow */
578   while (pcpu_scale >= 1024) {   while (pcpu_scale >= 1024) {
579   pcpu_scale /= 4;   pcpu_scale /= 4;
580   pcpu_shift -= 2;   pcpu_shift -= 2;
581   }   }
582     pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS? 20 : 2);
583   /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */   /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
584  #endif  #endif
  while (count-- > 0) {  
  div_t pmem = div((s->rss*pmem_scale) >> pmem_shift, 10);  
  int col = scr_width+1;  
  USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(div_t pcpu;)  
585    
586   if (s->rss >= 100*1024)   /* Ok, all preliminary data is ready, go through the list */
587   sprintf(rss_str_buf, "%6ldM", s->rss/1024);   scr_width += 2; /* account for leading '\n' and trailing NUL */
588     if (lines_rem > ntop)
589     lines_rem = ntop;
590     s = top;
591     while (--lines_rem >= 0) {
592     unsigned col;
593     CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
594    #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
595     CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
596    #endif
597    
598     if (s->vsz >= 100000)
599     sprintf(vsz_str_buf, "%6ldm", s->vsz/1024);
600   else   else
601   sprintf(rss_str_buf, "%7ld", s->rss);   sprintf(vsz_str_buf, "%7ld", s->vsz);
602   USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(   /* PID PPID USER STAT VSZ %MEM [%CPU] COMMAND */
603   pcpu = div((s->pcpu*pcpu_scale) >> pcpu_shift, 10);   col = snprintf(line_buf, scr_width,
604   )   "\n" "%5u%6u %-8.8s %s%s" FMT
605   col -= printf("\n%5u %-8s %s  "  #if ENABLE_FEATURE_TOP_SMP_PROCESS
606   "%s%6u"   " %3d"
607   USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE("%3u.%c")  #endif
608   "%3u.%c ",  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
609   s->pid, get_cached_username(s->uid), s->state,   FMT
610   rss_str_buf, s->ppid,  #endif
611   USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(pcpu.quot, '0'+pcpu.rem,)   " ",
612   pmem.quot, '0'+pmem.rem);   s->pid, s->ppid, get_cached_username(s->uid),
613   if (col > 0)   s->state, vsz_str_buf,
614   printf("%.*s", col, s->comm);   SHOW_STAT(pmem)
615    #if ENABLE_FEATURE_TOP_SMP_PROCESS
616     , s->last_seen_on_cpu
617    #endif
618    #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
619     , SHOW_STAT(pcpu)
620    #endif
621     );
622     if ((int)(col + 1) < scr_width)
623     read_cmdline(line_buf + col, scr_width - col - 1, s->pid, s->comm);
624     fputs(line_buf, stdout);
625   /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,   /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
626   jif.busy - prev_jif.busy, jif.total - prev_jif.total); */   cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */
627   s++;   s++;
628   }   }
629   /* printf(" %d", hist_iterations); */   /* printf(" %d", hist_iterations); */
630   putchar(OPT_BATCH_MODE ? '\n' : '\r');   bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
631   fflush(stdout);   fflush(stdout);
632  }  }
633    #undef UPSCALE
634    #undef SHOW_STAT
635    #undef CALC_STAT
636    #undef FMT
637    
638  static void clearmems(void)  static void clearmems(void)
639  {  {
640   clear_username_cache();   clear_username_cache();
641   free(top);   free(top);
642   top = 0;   top = NULL;
643   ntop = 0;   ntop = 0;
644  }  }
645    
   
646  #if ENABLE_FEATURE_USE_TERMIOS  #if ENABLE_FEATURE_USE_TERMIOS
647  #include <termios.h>  #include <termios.h>
648  #include <signal.h>  #include <signal.h>
649    
 static struct termios initial_settings;  
   
650  static void reset_term(void)  static void reset_term(void)
651  {  {
652   tcsetattr(0, TCSANOW, (void *) &initial_settings);   tcsetattr_stdin_TCSANOW(&initial_settings);
653  #if ENABLE_FEATURE_CLEAN_UP   if (ENABLE_FEATURE_CLEAN_UP) {
654   clearmems();   clearmems();
655  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
656   free(prev_hist);   free(prev_hist);
657  #endif  #endif
658  #endif /* FEATURE_CLEAN_UP */   }
659  }  }
660    
661  static void sig_catcher(int sig ATTRIBUTE_UNUSED)  static void sig_catcher(int sig UNUSED_PARAM)
662  {  {
663   reset_term();   reset_term();
664   exit(1);   exit(EXIT_FAILURE);
665  }  }
666  #endif /* FEATURE_USE_TERMIOS */  #endif /* FEATURE_USE_TERMIOS */
667    
668    /*
669     * TOPMEM support
670     */
671    
672    typedef unsigned long mem_t;
673    
674    typedef struct topmem_status_t {
675     unsigned pid;
676     char comm[COMM_LEN];
677     /* vsz doesn't count /dev/xxx mappings except /dev/zero */
678     mem_t vsz     ;
679     mem_t vszrw   ;
680     mem_t rss     ;
681     mem_t rss_sh  ;
682     mem_t dirty   ;
683     mem_t dirty_sh;
684     mem_t stack   ;
685    } topmem_status_t;
686    
687    enum { NUM_SORT_FIELD = 7 };
688    
689    #define topmem ((topmem_status_t*)top)
690    
691  int top_main(int argc, char **argv)  #if ENABLE_FEATURE_TOPMEM
692    
693    static int topmem_sort(char *a, char *b)
694    {
695     int n;
696     mem_t l, r;
697    
698     n = offsetof(topmem_status_t, vsz) + (sort_field * sizeof(mem_t));
699     l = *(mem_t*)(a + n);
700     r = *(mem_t*)(b + n);
701    // if (l == r) {
702    // l = a->mapped_rw;
703    // r = b->mapped_rw;
704    // }
705     /* We want to avoid unsigned->signed and truncation errors */
706     /* l>r: -1, l=r: 0, l<r: 1 */
707     n = (l > r) ? -1 : (l != r);
708     return inverted ? -n : n;
709    }
710    
711    /* Cut "NNNN " out of "    NNNN kb" */
712    static char *grab_number(char *str, const char *match, unsigned sz)
713    {
714     if (strncmp(str, match, sz) == 0) {
715     str = skip_whitespace(str + sz);
716     (skip_non_whitespace(str))[1] = '\0';
717     return xstrdup(str);
718     }
719     return NULL;
720    }
721    
722    /* display header info (meminfo / loadavg) */
723    static void display_topmem_header(int scr_width, int *lines_rem_p)
724    {
725     char linebuf[128];
726     unsigned i;
727     FILE *fp;
728     union {
729     struct {
730     /*  1 */ char *total;
731     /*  2 */ char *mfree;
732     /*  3 */ char *buf;
733     /*  4 */ char *cache;
734     /*  5 */ char *swaptotal;
735     /*  6 */ char *swapfree;
736     /*  7 */ char *dirty;
737     /*  8 */ char *mwrite;
738     /*  9 */ char *anon;
739     /* 10 */ char *map;
740     /* 11 */ char *slab;
741     } u;
742     char *str[11];
743     } Z;
744    #define total     Z.u.total
745    #define mfree     Z.u.mfree
746    #define buf       Z.u.buf
747    #define cache     Z.u.cache
748    #define swaptotal Z.u.swaptotal
749    #define swapfree  Z.u.swapfree
750    #define dirty     Z.u.dirty
751    #define mwrite    Z.u.mwrite
752    #define anon      Z.u.anon
753    #define map       Z.u.map
754    #define slab      Z.u.slab
755    #define str       Z.str
756    
757     memset(&Z, 0, sizeof(Z));
758    
759     /* read memory info */
760     fp = xfopen_for_read("meminfo");
761     while (fgets(linebuf, sizeof(linebuf), fp)) {
762     char *p;
763    
764    #define SCAN(match, name) \
765     p = grab_number(linebuf, match, sizeof(match)-1); \
766     if (p) { name = p; continue; }
767    
768     SCAN("MemTotal:", total);
769     SCAN("MemFree:", mfree);
770     SCAN("Buffers:", buf);
771     SCAN("Cached:", cache);
772     SCAN("SwapTotal:", swaptotal);
773     SCAN("SwapFree:", swapfree);
774     SCAN("Dirty:", dirty);
775     SCAN("Writeback:", mwrite);
776     SCAN("AnonPages:", anon);
777     SCAN("Mapped:", map);
778     SCAN("Slab:", slab);
779    #undef SCAN
780     }
781     fclose(fp);
782    
783    #define S(s) (s ? s : "0 ")
784     snprintf(linebuf, sizeof(linebuf),
785     "Mem %stotal %sanon %smap %sfree",
786     S(total), S(anon), S(map), S(mfree));
787     printf(OPT_BATCH_MODE ? "%.*s\n" : "\e[H\e[J%.*s\n", scr_width, linebuf);
788    
789     snprintf(linebuf, sizeof(linebuf),
790     " %sslab %sbuf %scache %sdirty %swrite",
791     S(slab), S(buf), S(cache), S(dirty), S(mwrite));
792     printf("%.*s\n", scr_width, linebuf);
793    
794     snprintf(linebuf, sizeof(linebuf),
795     "Swap %stotal %sfree", // TODO: % used?
796     S(swaptotal), S(swapfree));
797     printf("%.*s\n", scr_width, linebuf);
798    
799     (*lines_rem_p) -= 3;
800    #undef S
801    
802     for (i = 0; i < ARRAY_SIZE(str); i++)
803     free(str[i]);
804    #undef total
805    #undef free
806    #undef buf
807    #undef cache
808    #undef swaptotal
809    #undef swapfree
810    #undef dirty
811    #undef write
812    #undef anon
813    #undef map
814    #undef slab
815    #undef str
816    }
817    
818    static void ulltoa6_and_space(unsigned long long ul, char buf[6])
819    {
820     /* see http://en.wikipedia.org/wiki/Tera */
821     smart_ulltoa5(ul, buf, " mgtpezy");
822     buf[5] = ' ';
823    }
824    
825    static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width)
826  {  {
827   int count, lines, col;  #define HDR_STR "  PID   VSZ VSZRW   RSS (SHR) DIRTY (SHR) STACK"
828   unsigned interval = 5; /* default update rate is 5 seconds */  #define MIN_WIDTH sizeof(HDR_STR)
829   unsigned iterations = UINT_MAX; /* 2^32 iterations by default :) */   const topmem_status_t *s = topmem;
830   char *sinterval, *siterations;  
831     display_topmem_header(scr_width, &lines_rem);
832     strcpy(line_buf, HDR_STR " COMMAND");
833     line_buf[5 + sort_field * 6] = '*';
834     printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf);
835     lines_rem--;
836    
837     if (lines_rem > ntop)
838     lines_rem = ntop;
839     while (--lines_rem >= 0) {
840     /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
841     ulltoa6_and_space(s->pid     , &line_buf[0*6]);
842     ulltoa6_and_space(s->vsz     , &line_buf[1*6]);
843     ulltoa6_and_space(s->vszrw   , &line_buf[2*6]);
844     ulltoa6_and_space(s->rss     , &line_buf[3*6]);
845     ulltoa6_and_space(s->rss_sh  , &line_buf[4*6]);
846     ulltoa6_and_space(s->dirty   , &line_buf[5*6]);
847     ulltoa6_and_space(s->dirty_sh, &line_buf[6*6]);
848     ulltoa6_and_space(s->stack   , &line_buf[7*6]);
849     line_buf[8*6] = '\0';
850     if (scr_width > (int)MIN_WIDTH) {
851     read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
852     }
853     printf("\n""%.*s", scr_width, line_buf);
854     s++;
855     }
856     bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
857     fflush(stdout);
858    #undef HDR_STR
859    #undef MIN_WIDTH
860    }
861    
862    #else
863    void display_topmem_process_list(int lines_rem, int scr_width);
864    int topmem_sort(char *a, char *b);
865    #endif /* TOPMEM */
866    
867    /*
868     * end TOPMEM support
869     */
870    
871    enum {
872     TOP_MASK = 0
873     | PSSCAN_PID
874     | PSSCAN_PPID
875     | PSSCAN_VSZ
876     | PSSCAN_STIME
877     | PSSCAN_UTIME
878     | PSSCAN_STATE
879     | PSSCAN_COMM
880    #if ENABLE_FEATURE_TOP_SMP_PROCESS
881     | PSSCAN_CPU
882    #endif
883     | PSSCAN_UIDGID,
884     TOPMEM_MASK = 0
885     | PSSCAN_PID
886     | PSSCAN_SMAPS
887     | PSSCAN_COMM,
888    };
889    
890    int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
891    int top_main(int argc UNUSED_PARAM, char **argv)
892    {
893     int iterations;
894     unsigned lines, col;
895     int lines_rem;
896     unsigned interval;
897     char *str_interval, *str_iterations;
898     SKIP_FEATURE_TOPMEM(const) unsigned scan_mask = TOP_MASK;
899  #if ENABLE_FEATURE_USE_TERMIOS  #if ENABLE_FEATURE_USE_TERMIOS
900   struct termios new_settings;   struct termios new_settings;
901   struct timeval tv;   struct pollfd pfd[1];
  fd_set readfds;  
902   unsigned char c;   unsigned char c;
903    
904     pfd[0].fd = 0;
905     pfd[0].events = POLLIN;
906  #endif /* FEATURE_USE_TERMIOS */  #endif /* FEATURE_USE_TERMIOS */
907    
908   /* do normal option parsing */   INIT_G();
909   interval = 5;  
910     interval = 5; /* default update interval is 5 seconds */
911     iterations = 0; /* infinite */
912    #if ENABLE_FEATURE_TOP_SMP_CPU
913     /*num_cpus = 0;*/
914     /*smp_cpu_info = 0;*/  /* to start with show aggregate */
915     cpu_jif = &cur_jif;
916     cpu_prev_jif = &prev_jif;
917    #endif
918    
919     /* all args are options; -n NUM */
920   opt_complementary = "-";   opt_complementary = "-";
921   getopt32(argc, argv, "d:n:b", &sinterval, &siterations);   col = getopt32(argv, "d:n:b", &str_interval, &str_iterations);
922   if (option_mask32 & 0x1) interval = xatou(sinterval); // -d   if (col & OPT_d) {
923   if (option_mask32 & 0x2) iterations = xatou(siterations); // -n   /* work around for "-d 1" -> "-d -1" done by getopt32 */
924   //if (option_mask32 & 0x4) // -b   if (str_interval[0] == '-')
925     str_interval++;
926     /* Need to limit it to not overflow poll timeout */
927     interval = xatou16(str_interval);
928     }
929     if (col & OPT_n) {
930     if (str_iterations[0] == '-')
931     str_iterations++;
932     iterations = xatou(str_iterations);
933     }
934    
935   /* change to /proc */   /* change to /proc */
936   xchdir("/proc");   xchdir("/proc");
937  #if ENABLE_FEATURE_USE_TERMIOS  #if ENABLE_FEATURE_USE_TERMIOS
938   tcgetattr(0, (void *) &initial_settings);   tcgetattr(0, (void *) &initial_settings);
939   memcpy(&new_settings, &initial_settings, sizeof(struct termios));   memcpy(&new_settings, &initial_settings, sizeof(new_settings));
940   /* unbuffered input, turn off echo */   /* unbuffered input, turn off echo */
941   new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);   new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
942    
943   signal(SIGTERM, sig_catcher);   bb_signals(BB_FATAL_SIGS, sig_catcher);
944   signal(SIGINT, sig_catcher);   tcsetattr_stdin_TCSANOW(&new_settings);
  tcsetattr(0, TCSANOW, (void *) &new_settings);  
  atexit(reset_term);  
945  #endif /* FEATURE_USE_TERMIOS */  #endif /* FEATURE_USE_TERMIOS */
946    
947  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
# Line 451  int top_main(int argc, char **argv) Line 949  int top_main(int argc, char **argv)
949   sort_function[1] = mem_sort;   sort_function[1] = mem_sort;
950   sort_function[2] = time_sort;   sort_function[2] = time_sort;
951  #else  #else
952   sort_function = mem_sort;   sort_function[0] = mem_sort;
953  #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */  #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
954    
955   while (1) {   while (1) {
956   procps_status_t *p = NULL;   procps_status_t *p = NULL;
957    
958   /* Default to 25 lines - 5 lines for status */   lines = 24; /* default */
  lines = 24 - 3;  
959   col = 79;   col = 79;
960  #if ENABLE_FEATURE_USE_TERMIOS  #if ENABLE_FEATURE_USE_TERMIOS
961   get_terminal_width_height(0, &col, &lines);   /* We output to stdout, we need size of stdout (not stdin)! */
962   if (lines < 5 || col < MIN_WIDTH) {   get_terminal_width_height(STDOUT_FILENO, &col, &lines);
963     if (lines < 5 || col < 10) {
964   sleep(interval);   sleep(interval);
965   continue;   continue;
966   }   }
  lines -= 3;  
967  #endif /* FEATURE_USE_TERMIOS */  #endif /* FEATURE_USE_TERMIOS */
968     if (col > LINE_BUF_SIZE-2) /* +2 bytes for '\n', NUL, */
969     col = LINE_BUF_SIZE-2;
970    
971   /* read process IDs & status for all the processes */   /* read process IDs & status for all the processes */
972   while ((p = procps_scan(p, 0   while ((p = procps_scan(p, scan_mask)) != NULL) {
973   | PSSCAN_PID   int n;
974   | PSSCAN_PPID   if (scan_mask == TOP_MASK) {
975   | PSSCAN_RSS   n = ntop;
976   | PSSCAN_STIME   top = xrealloc_vector(top, 6, ntop++);
977   | PSSCAN_UTIME   top[n].pid = p->pid;
978   | PSSCAN_STATE   top[n].ppid = p->ppid;
979   | PSSCAN_COMM   top[n].vsz = p->vsz;
980   | PSSCAN_SID  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
981   | PSSCAN_UIDGID   top[n].ticks = p->stime + p->utime;
982   ))) {  #endif
983   int n = ntop;   top[n].uid = p->uid;
984   top = xrealloc(top, (++ntop)*sizeof(top_status_t));   strcpy(top[n].state, p->state);
985   top[n].pid = p->pid;   strcpy(top[n].comm, p->comm);
986   top[n].ppid = p->ppid;  #if ENABLE_FEATURE_TOP_SMP_PROCESS
987   top[n].rss = p->rss;   top[n].last_seen_on_cpu = p->last_seen_on_cpu;
988  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE  #endif
989   top[n].ticks = p->stime + p->utime;   } else { /* TOPMEM */
990  #endif  #if ENABLE_FEATURE_TOPMEM
991   top[n].uid = p->uid;   if (!(p->mapped_ro | p->mapped_rw))
992   strcpy(top[n].state, p->state);   continue; /* kernel threads are ignored */
993   strcpy(top[n].comm, p->comm);   n = ntop;
994   }   /* No bug here - top and topmem are the same */
995     top = xrealloc_vector(topmem, 6, ntop++);
996     strcpy(topmem[n].comm, p->comm);
997     topmem[n].pid      = p->pid;
998     topmem[n].vsz      = p->mapped_rw + p->mapped_ro;
999     topmem[n].vszrw    = p->mapped_rw;
1000     topmem[n].rss_sh   = p->shared_clean + p->shared_dirty;
1001     topmem[n].rss      = p->private_clean + p->private_dirty + topmem[n].rss_sh;
1002     topmem[n].dirty    = p->private_dirty + p->shared_dirty;
1003     topmem[n].dirty_sh = p->shared_dirty;
1004     topmem[n].stack    = p->stack;
1005    #endif
1006     }
1007     } /* end of "while we read /proc" */
1008   if (ntop == 0) {   if (ntop == 0) {
1009   bb_error_msg_and_die("can't find process info in /proc");   bb_error_msg("no process info in /proc");
1010     break;
1011   }   }
1012    
1013     if (scan_mask == TOP_MASK) {
1014  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1015   if (!prev_hist_count) {   if (!prev_hist_count) {
1016     do_stats();
1017     usleep(100000);
1018     clearmems();
1019     continue;
1020     }
1021   do_stats();   do_stats();
1022   sleep(1);   /* TODO: we don't need to sort all 10000 processes, we need to find top 24! */
1023   clearmems();   qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
  continue;  
  }  
  do_stats();  
  qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);  
1024  #else  #else
1025   qsort(top, ntop, sizeof(top_status_t), (void*)sort_function);   qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
1026  #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */  #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
  count = lines;  
  if (OPT_BATCH_MODE || count > ntop) {  
  count = ntop;  
1027   }   }
1028   /* show status for each of the processes */  #if ENABLE_FEATURE_TOPMEM
1029   display_status(count, col);   else { /* TOPMEM */
1030  #if ENABLE_FEATURE_USE_TERMIOS   qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1031   tv.tv_sec = interval;   }
1032   tv.tv_usec = 0;  #endif
1033   FD_ZERO(&readfds);   lines_rem = lines;
1034   FD_SET(0, &readfds);   if (OPT_BATCH_MODE) {
1035   select(1, &readfds, NULL, NULL, &tv);   lines_rem = INT_MAX;
1036   if (FD_ISSET(0, &readfds)) {   }
1037   if (read(0, &c, 1) <= 0) {   /* signal */   if (scan_mask == TOP_MASK)
1038   return EXIT_FAILURE;   display_process_list(lines_rem, col);
1039    #if ENABLE_FEATURE_TOPMEM
1040     else
1041     display_topmem_process_list(lines_rem, col);
1042    #endif
1043     clearmems();
1044     if (iterations >= 0 && !--iterations)
1045     break;
1046    #if !ENABLE_FEATURE_USE_TERMIOS
1047     sleep(interval);
1048    #else
1049     if (option_mask32 & (OPT_b|OPT_EOF))
1050     /* batch mode, or EOF on stdin ("top </dev/null") */
1051     sleep(interval);
1052     else if (safe_poll(pfd, 1, interval * 1000) > 0) {
1053     if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */
1054     option_mask32 |= OPT_EOF;
1055     continue;
1056   }   }
1057   if (c == 'q' || c == initial_settings.c_cc[VINTR])   if (c == initial_settings.c_cc[VINTR])
1058   break;   break;
1059   if (c == 'M') {   c |= 0x20; /* lowercase */
1060  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE   if (c == 'q')
1061     break;
1062     if (c == 'n') {
1063     USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1064     sort_function[0] = pid_sort;
1065     }
1066     if (c == 'm') {
1067     USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1068   sort_function[0] = mem_sort;   sort_function[0] = mem_sort;
1069    #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1070   sort_function[1] = pcpu_sort;   sort_function[1] = pcpu_sort;
1071   sort_function[2] = time_sort;   sort_function[2] = time_sort;
 #else  
  sort_function = mem_sort;  
1072  #endif  #endif
1073   }   }
1074  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1075   if (c == 'P') {   if (c == 'p') {
1076     USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1077   sort_function[0] = pcpu_sort;   sort_function[0] = pcpu_sort;
1078   sort_function[1] = mem_sort;   sort_function[1] = mem_sort;
1079   sort_function[2] = time_sort;   sort_function[2] = time_sort;
1080   }   }
1081   if (c == 'T') {   if (c == 't') {
1082     USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1083   sort_function[0] = time_sort;   sort_function[0] = time_sort;
1084   sort_function[1] = mem_sort;   sort_function[1] = mem_sort;
1085   sort_function[2] = pcpu_sort;   sort_function[2] = pcpu_sort;
1086   }   }
1087    #if ENABLE_FEATURE_TOPMEM
1088     if (c == 's') {
1089     scan_mask = TOPMEM_MASK;
1090     free(prev_hist);
1091     prev_hist = NULL;
1092     prev_hist_count = 0;
1093     sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1094     }
1095     if (c == 'r')
1096     inverted ^= 1;
1097  #endif  #endif
1098   if (c == 'N') {  #if ENABLE_FEATURE_TOP_SMP_CPU
1099  #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE   /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
1100   sort_function[0] = pid_sort;   if (c == 'c' || c == '1') {
1101  #else   /* User wants to toggle per cpu <> aggregate */
1102   sort_function = pid_sort;   if (smp_cpu_info) {
1103  #endif   free(cpu_prev_jif);
1104     free(cpu_jif);
1105     cpu_jif = &cur_jif;
1106     cpu_prev_jif = &prev_jif;
1107     } else {
1108     /* Prepare for xrealloc() */
1109     cpu_jif = cpu_prev_jif = NULL;
1110     }
1111     num_cpus = 0;
1112     smp_cpu_info = !smp_cpu_info;
1113     get_jiffy_counts();
1114   }   }
1115    #endif
1116    #endif
1117   }   }
  if (!--iterations)  
  break;  
 #else  
  sleep(interval);  
1118  #endif /* FEATURE_USE_TERMIOS */  #endif /* FEATURE_USE_TERMIOS */
1119   clearmems();   } /* end of "while (1)" */
1120   }  
1121   if (ENABLE_FEATURE_CLEAN_UP)   bb_putchar('\n');
1122   clearmems();  #if ENABLE_FEATURE_USE_TERMIOS
1123   putchar('\n');   reset_term();
1124    #endif
1125   return EXIT_SUCCESS;   return EXIT_SUCCESS;
1126  }  }

Legend:
Removed from v.815  
changed lines
  Added in v.816