1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118#include "libbb.h"
119
120#define ESC "\033"
121
122typedef struct top_status_t {
123 unsigned long vsz;
124#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
125 unsigned long ticks;
126 unsigned pcpu;
127#endif
128 unsigned pid, ppid;
129 unsigned uid;
130 char state[4];
131 char comm[COMM_LEN];
132#if ENABLE_FEATURE_TOP_SMP_PROCESS
133 int last_seen_on_cpu;
134#endif
135} top_status_t;
136
137typedef struct jiffy_counts_t {
138
139 unsigned long long usr, nic, sys, idle;
140 unsigned long long iowait, irq, softirq, steal;
141 unsigned long long total;
142 unsigned long long busy;
143} jiffy_counts_t;
144
145
146
147typedef struct save_hist {
148 unsigned long ticks;
149 pid_t pid;
150} save_hist;
151
152typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
153
154
155enum { SORT_DEPTH = 3 };
156
157
158enum { LINE_BUF_SIZE = 512 - 64 };
159
160struct globals {
161 top_status_t *top;
162 int ntop;
163 smallint inverted;
164#if ENABLE_FEATURE_TOPMEM
165 smallint sort_field;
166#endif
167#if ENABLE_FEATURE_TOP_SMP_CPU
168 smallint smp_cpu_info;
169#endif
170 unsigned lines;
171#if ENABLE_FEATURE_TOP_INTERACTIVE
172 struct termios initial_settings;
173 int scroll_ofs;
174#define G_scroll_ofs G.scroll_ofs
175#else
176#define G_scroll_ofs 0
177#endif
178#if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
179 cmp_funcp sort_function[1];
180#else
181 cmp_funcp sort_function[SORT_DEPTH];
182 struct save_hist *prev_hist;
183 unsigned prev_hist_count;
184 jiffy_counts_t cur_jif, prev_jif;
185
186 unsigned total_pcpu;
187
188#endif
189#if ENABLE_FEATURE_TOP_SMP_CPU
190
191 jiffy_counts_t *cpu_jif, *cpu_prev_jif;
192 unsigned num_cpus;
193#endif
194#if ENABLE_FEATURE_TOP_INTERACTIVE
195 char kbd_input[KEYCODE_BUFFER_SIZE];
196#endif
197 char line_buf[LINE_BUF_SIZE];
198};
199#define G (*ptr_to_globals)
200#define top (G.top )
201#define ntop (G.ntop )
202#define sort_field (G.sort_field )
203#define inverted (G.inverted )
204#define smp_cpu_info (G.smp_cpu_info )
205#define initial_settings (G.initial_settings )
206#define sort_function (G.sort_function )
207#define prev_hist (G.prev_hist )
208#define prev_hist_count (G.prev_hist_count )
209#define cur_jif (G.cur_jif )
210#define prev_jif (G.prev_jif )
211#define cpu_jif (G.cpu_jif )
212#define cpu_prev_jif (G.cpu_prev_jif )
213#define num_cpus (G.num_cpus )
214#define total_pcpu (G.total_pcpu )
215#define line_buf (G.line_buf )
216#define INIT_G() do { \
217 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
218 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \
219} while (0)
220
221enum {
222 OPT_d = (1 << 0),
223 OPT_n = (1 << 1),
224 OPT_b = (1 << 2),
225 OPT_H = (1 << 3),
226 OPT_m = (1 << 4),
227 OPT_EOF = (1 << 5),
228};
229#define OPT_BATCH_MODE (option_mask32 & OPT_b)
230
231
232#if ENABLE_FEATURE_TOP_INTERACTIVE
233static int pid_sort(top_status_t *P, top_status_t *Q)
234{
235
236
237 return (Q->pid - P->pid);
238}
239#endif
240
241static int mem_sort(top_status_t *P, top_status_t *Q)
242{
243
244 if (Q->vsz < P->vsz) return -1;
245 return Q->vsz != P->vsz;
246}
247
248
249#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
250
251static int pcpu_sort(top_status_t *P, top_status_t *Q)
252{
253
254
255 return (int)Q->pcpu - (int)P->pcpu;
256}
257
258static int time_sort(top_status_t *P, top_status_t *Q)
259{
260
261 if (Q->ticks < P->ticks) return -1;
262 return Q->ticks != P->ticks;
263}
264
265static int mult_lvl_cmp(void* a, void* b)
266{
267 int i, cmp_val;
268
269 for (i = 0; i < SORT_DEPTH; i++) {
270 cmp_val = (*sort_function[i])(a, b);
271 if (cmp_val != 0)
272 break;
273 }
274 return inverted ? -cmp_val : cmp_val;
275}
276
277static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
278{
279#if !ENABLE_FEATURE_TOP_SMP_CPU
280 static const char fmt[] ALIGN1 = "cpu %llu %llu %llu %llu %llu %llu %llu %llu";
281#else
282 static const char fmt[] ALIGN1 = "cp%*s %llu %llu %llu %llu %llu %llu %llu %llu";
283#endif
284 int ret;
285
286 if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' )
287 return 0;
288 ret = sscanf(line_buf, fmt,
289 &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle,
290 &p_jif->iowait, &p_jif->irq, &p_jif->softirq,
291 &p_jif->steal);
292 if (ret >= 4) {
293 p_jif->total = p_jif->usr + p_jif->nic + p_jif->sys + p_jif->idle
294 + p_jif->iowait + p_jif->irq + p_jif->softirq + p_jif->steal;
295
296 p_jif->busy = p_jif->total - p_jif->idle - p_jif->iowait;
297 }
298
299 return ret;
300}
301
302static void get_jiffy_counts(void)
303{
304 FILE* fp = xfopen_for_read("stat");
305
306
307
308 prev_jif = cur_jif;
309 if (read_cpu_jiffy(fp, &cur_jif) < 4)
310 bb_error_msg_and_die("can't read '%s'", "/proc/stat");
311
312#if !ENABLE_FEATURE_TOP_SMP_CPU
313 fclose(fp);
314 return;
315#else
316 if (!smp_cpu_info) {
317 fclose(fp);
318 return;
319 }
320
321 if (!num_cpus) {
322
323
324
325 while (1) {
326 cpu_jif = xrealloc_vector(cpu_jif, 1, num_cpus);
327 if (read_cpu_jiffy(fp, &cpu_jif[num_cpus]) <= 4)
328 break;
329 num_cpus++;
330 }
331 if (num_cpus == 0)
332 smp_cpu_info = 0;
333
334 cpu_prev_jif = xzalloc(sizeof(cpu_prev_jif[0]) * num_cpus);
335
336
337 usleep(50000);
338 } else {
339 jiffy_counts_t *tmp;
340 int i;
341
342
343 tmp = cpu_prev_jif;
344 cpu_prev_jif = cpu_jif;
345 cpu_jif = tmp;
346
347
348 for (i = 0; i < num_cpus; i++)
349 read_cpu_jiffy(fp, &cpu_jif[i]);
350 }
351#endif
352 fclose(fp);
353}
354
355static void do_stats(void)
356{
357 top_status_t *cur;
358 pid_t pid;
359 int n;
360 unsigned i, last_i;
361 struct save_hist *new_hist;
362
363 get_jiffy_counts();
364 total_pcpu = 0;
365
366 new_hist = xmalloc(sizeof(new_hist[0]) * ntop);
367
368
369
370
371 i = 0;
372 for (n = 0; n < ntop; n++) {
373 cur = top + n;
374
375
376
377
378
379 pid = cur->pid;
380 new_hist[n].ticks = cur->ticks;
381 new_hist[n].pid = pid;
382
383
384 cur->pcpu = 0;
385
386
387 last_i = i;
388 if (prev_hist_count) do {
389 if (prev_hist[i].pid == pid) {
390 cur->pcpu = cur->ticks - prev_hist[i].ticks;
391 total_pcpu += cur->pcpu;
392 break;
393 }
394 i = (i+1) % prev_hist_count;
395
396 } while (i != last_i);
397
398 }
399
400
401
402
403 free(prev_hist);
404 prev_hist = new_hist;
405 prev_hist_count = ntop;
406}
407
408#endif
409
410#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
411
412static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
413{
414 unsigned t;
415 if (value >= total) {
416 strcpy(pbuf, " 100% ");
417 return pbuf;
418 }
419
420 value = 1000 * value / total;
421 t = value / 100;
422 value = value % 100;
423 pbuf[0] = ' ';
424 pbuf[1] = t ? t + '0' : ' ';
425 pbuf[2] = '0' + (value / 10);
426 pbuf[3] = '.';
427 pbuf[4] = '0' + (value % 10);
428 pbuf[5] = '%';
429 pbuf[6] = ' ';
430 pbuf[7] = '\0';
431 return pbuf;
432}
433#endif
434
435#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
436static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
437{
438
439
440
441 unsigned total_diff;
442 jiffy_counts_t *p_jif, *p_prev_jif;
443 int i;
444# if ENABLE_FEATURE_TOP_SMP_CPU
445 int n_cpu_lines;
446# endif
447
448
449# define CALC_TOTAL_DIFF do { \
450 total_diff = (unsigned)(p_jif->total - p_prev_jif->total); \
451 if (total_diff == 0) total_diff = 1; \
452} while (0)
453
454# if ENABLE_FEATURE_TOP_DECIMALS
455# define CALC_STAT(xxx) char xxx[8]
456# define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(p_jif->xxx - p_prev_jif->xxx), total_diff)
457# define FMT "%s"
458# else
459# define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(p_jif->xxx - p_prev_jif->xxx) / total_diff
460# define SHOW_STAT(xxx) xxx
461# define FMT "%4u%% "
462# endif
463
464# if !ENABLE_FEATURE_TOP_SMP_CPU
465 {
466 i = 1;
467 p_jif = &cur_jif;
468 p_prev_jif = &prev_jif;
469# else
470
471 n_cpu_lines = smp_cpu_info ? num_cpus : 1;
472 if (n_cpu_lines > *lines_rem_p)
473 n_cpu_lines = *lines_rem_p;
474
475 for (i = 0; i < n_cpu_lines; i++) {
476 p_jif = &cpu_jif[i];
477 p_prev_jif = &cpu_prev_jif[i];
478# endif
479 CALC_TOTAL_DIFF;
480
481 {
482 CALC_STAT(usr);
483 CALC_STAT(sys);
484 CALC_STAT(nic);
485 CALC_STAT(idle);
486 CALC_STAT(iowait);
487 CALC_STAT(irq);
488 CALC_STAT(softirq);
489
490
491 snprintf(scrbuf, scr_width,
492
493# if ENABLE_FEATURE_TOP_SMP_CPU
494 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
495 (smp_cpu_info ? utoa(i) : ""),
496# else
497 "CPU:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
498# endif
499 SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle),
500 SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq)
501
502
503 );
504 puts(scrbuf);
505 }
506 }
507# undef SHOW_STAT
508# undef CALC_STAT
509# undef FMT
510 *lines_rem_p -= i;
511}
512#else
513# define display_cpus(scr_width, scrbuf, lines_rem) ((void)0)
514#endif
515
516enum {
517 MI_MEMTOTAL,
518 MI_MEMFREE,
519 MI_MEMSHARED,
520 MI_SHMEM,
521 MI_BUFFERS,
522 MI_CACHED,
523 MI_SWAPTOTAL,
524 MI_SWAPFREE,
525 MI_DIRTY,
526 MI_WRITEBACK,
527 MI_ANONPAGES,
528 MI_MAPPED,
529 MI_SLAB,
530 MI_MAX
531};
532
533static void parse_meminfo(unsigned long meminfo[MI_MAX])
534{
535 static const char fields[] ALIGN1 =
536 "MemTotal\0"
537 "MemFree\0"
538 "MemShared\0"
539 "Shmem\0"
540 "Buffers\0"
541 "Cached\0"
542 "SwapTotal\0"
543 "SwapFree\0"
544 "Dirty\0"
545 "Writeback\0"
546 "AnonPages\0"
547 "Mapped\0"
548 "Slab\0";
549 char buf[60];
550 FILE *f;
551 int i;
552
553 memset(meminfo, 0, sizeof(meminfo[0]) * MI_MAX);
554 f = xfopen_for_read("meminfo");
555 while (fgets(buf, sizeof(buf), f) != NULL) {
556 char *c = strchr(buf, ':');
557 if (!c)
558 continue;
559 *c = '\0';
560 i = index_in_strings(fields, buf);
561 if (i >= 0)
562 meminfo[i] = strtoul(c+1, NULL, 10);
563 }
564 fclose(f);
565}
566
567static unsigned long display_header(int scr_width, int *lines_rem_p)
568{
569 char scrbuf[100];
570 char *buf;
571 unsigned long meminfo[MI_MAX];
572
573 parse_meminfo(meminfo);
574
575
576 if (scr_width > (int)sizeof(scrbuf))
577 scr_width = sizeof(scrbuf);
578 snprintf(scrbuf, scr_width,
579 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
580 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE],
581 meminfo[MI_MEMFREE],
582 meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM],
583 meminfo[MI_BUFFERS],
584 meminfo[MI_CACHED]);
585
586 printf(OPT_BATCH_MODE ? "%s\n" : ESC"[H" ESC"[J" "%s\n", scrbuf);
587 (*lines_rem_p)--;
588
589
590
591
592 display_cpus(scr_width, scrbuf, lines_rem_p);
593
594
595 buf = stpcpy(scrbuf, "Load average: ");
596 open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: "));
597 scrbuf[scr_width - 1] = '\0';
598 strchrnul(buf, '\n')[0] = '\0';
599 puts(scrbuf);
600 (*lines_rem_p)--;
601
602 return meminfo[MI_MEMTOTAL];
603}
604
605static NOINLINE void display_process_list(int lines_rem, int scr_width)
606{
607 enum {
608 BITS_PER_INT = sizeof(int) * 8
609 };
610
611 top_status_t *s;
612 unsigned long total_memory = display_header(scr_width, &lines_rem);
613
614
615 unsigned pmem_shift, pmem_scale, pmem_half;
616#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
617 unsigned tmp_unsigned;
618 unsigned pcpu_shift, pcpu_scale, pcpu_half;
619 unsigned busy_jifs;
620#endif
621
622
623 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width,
624 " PID PPID USER STAT VSZ %VSZ"
625 IF_FEATURE_TOP_SMP_PROCESS(" CPU")
626 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
627 " COMMAND");
628 lines_rem--;
629
630#if ENABLE_FEATURE_TOP_DECIMALS
631# define UPSCALE 1000
632# define CALC_STAT(name, val) div_t name = div((val), 10)
633# define SHOW_STAT(name) name.quot, '0'+name.rem
634# define FMT "%3u.%c"
635#else
636# define UPSCALE 100
637# define CALC_STAT(name, val) unsigned name = (val)
638# define SHOW_STAT(name) name
639# define FMT "%4u%%"
640#endif
641
642
643
644 pmem_shift = BITS_PER_INT-11;
645 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
646
647 while (pmem_scale >= 512) {
648 pmem_scale /= 4;
649 pmem_shift -= 2;
650 }
651 pmem_half = (1U << pmem_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
652#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
653 busy_jifs = cur_jif.busy - prev_jif.busy;
654
655
656 if (total_pcpu < busy_jifs) total_pcpu = busy_jifs;
657
658
659
660
661
662
663
664
665
666 pcpu_shift = 6;
667 pcpu_scale = UPSCALE*64 * (uint16_t)busy_jifs;
668 if (pcpu_scale == 0)
669 pcpu_scale = 1;
670 while (pcpu_scale < (1U << (BITS_PER_INT-2))) {
671 pcpu_scale *= 4;
672 pcpu_shift += 2;
673 }
674 tmp_unsigned = (uint16_t)(cur_jif.total - prev_jif.total) * total_pcpu;
675 if (tmp_unsigned != 0)
676 pcpu_scale /= tmp_unsigned;
677
678 while (pcpu_scale >= 1024) {
679 pcpu_scale /= 4;
680 pcpu_shift -= 2;
681 }
682 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
683
684#endif
685
686
687 scr_width += 2;
688 if (lines_rem > ntop - G_scroll_ofs)
689 lines_rem = ntop - G_scroll_ofs;
690 s = top + G_scroll_ofs;
691 while (--lines_rem >= 0) {
692 char vsz_str_buf[8];
693 unsigned col;
694
695 CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
696#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
697 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
698#endif
699
700 smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy");
701
702 col = snprintf(line_buf, scr_width,
703 "\n" "%5u%6u %-8.8s %s %.5s" FMT
704 IF_FEATURE_TOP_SMP_PROCESS(" %3d")
705 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
706 " ",
707 s->pid, s->ppid, get_cached_username(s->uid),
708 s->state, vsz_str_buf,
709 SHOW_STAT(pmem)
710 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
711 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
712 );
713 if ((int)(scr_width - col) > 1)
714 read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm);
715 fputs(line_buf, stdout);
716
717
718 s++;
719 }
720
721 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
722 fflush_all();
723}
724#undef UPSCALE
725#undef SHOW_STAT
726#undef CALC_STAT
727#undef FMT
728
729static void clearmems(void)
730{
731 clear_username_cache();
732 free(top);
733 top = NULL;
734}
735
736#if ENABLE_FEATURE_TOP_INTERACTIVE
737static void reset_term(void)
738{
739 if (!OPT_BATCH_MODE)
740 tcsetattr_stdin_TCSANOW(&initial_settings);
741}
742
743static void sig_catcher(int sig)
744{
745 reset_term();
746 kill_myself_with_sig(sig);
747}
748#endif
749
750
751
752
753
754typedef unsigned long mem_t;
755
756typedef struct topmem_status_t {
757 unsigned pid;
758 char comm[COMM_LEN];
759
760 mem_t vsz ;
761 mem_t vszrw ;
762 mem_t rss ;
763 mem_t rss_sh ;
764 mem_t dirty ;
765 mem_t dirty_sh;
766 mem_t stack ;
767} topmem_status_t;
768
769enum { NUM_SORT_FIELD = 7 };
770
771#define topmem ((topmem_status_t*)top)
772
773#if ENABLE_FEATURE_TOPMEM
774
775static int topmem_sort(char *a, char *b)
776{
777 int n;
778 mem_t l, r;
779
780 n = offsetof(topmem_status_t, vsz) + (sort_field * sizeof(mem_t));
781 l = *(mem_t*)(a + n);
782 r = *(mem_t*)(b + n);
783 if (l == r) {
784 l = ((topmem_status_t*)a)->dirty;
785 r = ((topmem_status_t*)b)->dirty;
786 }
787
788
789 n = (l > r) ? -1 : (l != r);
790 return inverted ? -n : n;
791}
792
793
794static void display_topmem_header(int scr_width, int *lines_rem_p)
795{
796 unsigned long meminfo[MI_MAX];
797
798 parse_meminfo(meminfo);
799
800 snprintf(line_buf, LINE_BUF_SIZE,
801 "Mem total:%lu anon:%lu map:%lu free:%lu",
802 meminfo[MI_MEMTOTAL],
803 meminfo[MI_ANONPAGES],
804 meminfo[MI_MAPPED],
805 meminfo[MI_MEMFREE]);
806 printf(OPT_BATCH_MODE ? "%.*s\n" : ESC"[H" ESC"[J" "%.*s\n", scr_width, line_buf);
807
808 snprintf(line_buf, LINE_BUF_SIZE,
809 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu",
810 meminfo[MI_SLAB],
811 meminfo[MI_BUFFERS],
812 meminfo[MI_CACHED],
813 meminfo[MI_DIRTY],
814 meminfo[MI_WRITEBACK]);
815 printf("%.*s\n", scr_width, line_buf);
816
817 snprintf(line_buf, LINE_BUF_SIZE,
818 "Swap total:%lu free:%lu",
819 meminfo[MI_SWAPTOTAL],
820 meminfo[MI_SWAPFREE]);
821 printf("%.*s\n", scr_width, line_buf);
822
823 (*lines_rem_p) -= 3;
824}
825
826static void ulltoa6_and_space(unsigned long long ul, char buf[6])
827{
828
829 smart_ulltoa5(ul, buf, " mgtpezy")[0] = ' ';
830}
831
832static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width)
833{
834#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK"
835#define MIN_WIDTH sizeof(HDR_STR)
836 const topmem_status_t *s = topmem + G_scroll_ofs;
837 char *cp, ch;
838
839 display_topmem_header(scr_width, &lines_rem);
840
841 strcpy(line_buf, HDR_STR " COMMAND");
842
843 cp = &line_buf[5 + sort_field * 6];
844 ch = "^_"[inverted];
845 cp[6] = ch;
846 do *cp++ = ch; while (*cp == ' ');
847
848 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width, line_buf);
849 lines_rem--;
850
851 if (lines_rem > ntop - G_scroll_ofs)
852 lines_rem = ntop - G_scroll_ofs;
853 while (--lines_rem >= 0) {
854
855 ulltoa6_and_space(s->pid , &line_buf[0*6]);
856 ulltoa6_and_space(s->vsz , &line_buf[1*6]);
857 ulltoa6_and_space(s->vszrw , &line_buf[2*6]);
858 ulltoa6_and_space(s->rss , &line_buf[3*6]);
859 ulltoa6_and_space(s->rss_sh , &line_buf[4*6]);
860 ulltoa6_and_space(s->dirty , &line_buf[5*6]);
861 ulltoa6_and_space(s->dirty_sh, &line_buf[6*6]);
862 ulltoa6_and_space(s->stack , &line_buf[7*6]);
863 line_buf[8*6] = '\0';
864 if (scr_width > (int)MIN_WIDTH) {
865 read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
866 }
867 printf("\n""%.*s", scr_width, line_buf);
868 s++;
869 }
870 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
871 fflush_all();
872#undef HDR_STR
873#undef MIN_WIDTH
874}
875
876#else
877void display_topmem_process_list(int lines_rem, int scr_width);
878int topmem_sort(char *a, char *b);
879#endif
880
881
882
883
884
885enum {
886 TOP_MASK = 0
887 | PSSCAN_PID
888 | PSSCAN_PPID
889 | PSSCAN_VSZ
890 | PSSCAN_STIME
891 | PSSCAN_UTIME
892 | PSSCAN_STATE
893 | PSSCAN_COMM
894 | PSSCAN_CPU
895 | PSSCAN_UIDGID,
896 TOPMEM_MASK = 0
897 | PSSCAN_PID
898 | PSSCAN_SMAPS
899 | PSSCAN_COMM,
900 EXIT_MASK = 0,
901 NO_RESCAN_MASK = (unsigned)-1,
902};
903
904#if ENABLE_FEATURE_TOP_INTERACTIVE
905static unsigned handle_input(unsigned scan_mask, duration_t interval)
906{
907 if (option_mask32 & OPT_EOF) {
908
909 sleep_for_duration(interval);
910 return scan_mask;
911 }
912
913 while (1) {
914 int32_t c;
915
916 c = read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
917 if (c == -1 && errno != EAGAIN) {
918
919 option_mask32 |= OPT_EOF;
920 break;
921 }
922 interval = 0;
923
924 if (c == initial_settings.c_cc[VINTR])
925 return EXIT_MASK;
926 if (c == initial_settings.c_cc[VEOF])
927 return EXIT_MASK;
928
929 if (c == KEYCODE_UP) {
930 G_scroll_ofs--;
931 goto normalize_ofs;
932 }
933 if (c == KEYCODE_DOWN) {
934 G_scroll_ofs++;
935 goto normalize_ofs;
936 }
937 if (c == KEYCODE_HOME) {
938 G_scroll_ofs = 0;
939 goto normalize_ofs;
940 }
941 if (c == KEYCODE_END) {
942 G_scroll_ofs = ntop - G.lines / 2;
943 goto normalize_ofs;
944 }
945 if (c == KEYCODE_PAGEUP) {
946 G_scroll_ofs -= G.lines / 2;
947 goto normalize_ofs;
948 }
949 if (c == KEYCODE_PAGEDOWN) {
950 G_scroll_ofs += G.lines / 2;
951 normalize_ofs:
952 if (G_scroll_ofs >= ntop)
953 G_scroll_ofs = ntop - 1;
954 if (G_scroll_ofs < 0)
955 G_scroll_ofs = 0;
956 return NO_RESCAN_MASK;
957 }
958
959 c |= 0x20;
960 if (c == 'q')
961 return EXIT_MASK;
962
963 if (c == 'n') {
964 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
965 sort_function[0] = pid_sort;
966 continue;
967 }
968 if (c == 'm') {
969 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
970 sort_function[0] = mem_sort;
971# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
972 sort_function[1] = pcpu_sort;
973 sort_function[2] = time_sort;
974# endif
975 continue;
976 }
977# if ENABLE_FEATURE_SHOW_THREADS
978 if (c == 'h'
979 IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
980 ) {
981 scan_mask ^= PSSCAN_TASKS;
982 continue;
983 }
984# endif
985# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
986 if (c == 'p') {
987 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
988 sort_function[0] = pcpu_sort;
989 sort_function[1] = mem_sort;
990 sort_function[2] = time_sort;
991 continue;
992 }
993 if (c == 't') {
994 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
995 sort_function[0] = time_sort;
996 sort_function[1] = mem_sort;
997 sort_function[2] = pcpu_sort;
998 continue;
999 }
1000# if ENABLE_FEATURE_TOPMEM
1001 if (c == 's') {
1002 scan_mask = TOPMEM_MASK;
1003 free(prev_hist);
1004 prev_hist = NULL;
1005 prev_hist_count = 0;
1006 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1007 continue;
1008 }
1009# endif
1010 if (c == 'r') {
1011 inverted ^= 1;
1012 continue;
1013 }
1014# if ENABLE_FEATURE_TOP_SMP_CPU
1015
1016 if (c == 'c' || c == '1') {
1017
1018 if (smp_cpu_info) {
1019 free(cpu_prev_jif);
1020 free(cpu_jif);
1021 cpu_jif = &cur_jif;
1022 cpu_prev_jif = &prev_jif;
1023 } else {
1024
1025 cpu_jif = cpu_prev_jif = NULL;
1026 }
1027 num_cpus = 0;
1028 smp_cpu_info = !smp_cpu_info;
1029 get_jiffy_counts();
1030 continue;
1031 }
1032# endif
1033# endif
1034 break;
1035 }
1036
1037 return scan_mask;
1038}
1039#endif
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1097int top_main(int argc UNUSED_PARAM, char **argv)
1098{
1099 duration_t interval;
1100 int iterations;
1101 unsigned col;
1102 char *str_interval, *str_iterations;
1103 unsigned scan_mask = TOP_MASK;
1104
1105 INIT_G();
1106
1107 interval = 5;
1108 iterations = 0;
1109#if ENABLE_FEATURE_TOP_SMP_CPU
1110
1111
1112 cpu_jif = &cur_jif;
1113 cpu_prev_jif = &prev_jif;
1114#endif
1115
1116
1117 make_all_argv_opts(argv);
1118 col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations);
1119
1120#if ENABLE_FEATURE_TOPMEM
1121 if (col & OPT_m)
1122 scan_mask = TOPMEM_MASK;
1123#endif
1124 if (col & OPT_d) {
1125
1126 if (str_interval[0] == '-')
1127 str_interval++;
1128 interval = parse_duration_str(str_interval);
1129
1130 if (interval > INT_MAX / 1000)
1131 interval = INT_MAX / 1000;
1132 }
1133 if (col & OPT_n) {
1134 if (str_iterations[0] == '-')
1135 str_iterations++;
1136 iterations = xatou(str_iterations);
1137 }
1138#if ENABLE_FEATURE_SHOW_THREADS
1139 if (col & OPT_H) {
1140 scan_mask |= PSSCAN_TASKS;
1141 }
1142#endif
1143
1144
1145 xchdir("/proc");
1146
1147#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1148 sort_function[0] = pcpu_sort;
1149 sort_function[1] = mem_sort;
1150 sort_function[2] = time_sort;
1151#else
1152 sort_function[0] = mem_sort;
1153#endif
1154
1155 if (OPT_BATCH_MODE) {
1156 option_mask32 |= OPT_EOF;
1157 }
1158#if ENABLE_FEATURE_TOP_INTERACTIVE
1159 else {
1160
1161 set_termios_to_raw(STDIN_FILENO, &initial_settings, TERMIOS_CLEAR_ISIG);
1162 die_func = reset_term;
1163 }
1164
1165 bb_signals(BB_FATAL_SIGS, sig_catcher);
1166
1167
1168 scan_mask = handle_input(scan_mask, 0);
1169#endif
1170
1171 while (scan_mask != EXIT_MASK) {
1172 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask;)
1173 procps_status_t *p = NULL;
1174
1175 if (OPT_BATCH_MODE) {
1176 G.lines = INT_MAX;
1177 col = LINE_BUF_SIZE - 2;
1178 } else {
1179 G.lines = 24;
1180 col = 79;
1181
1182 get_terminal_width_height(STDOUT_FILENO, &col, &G.lines);
1183 if (G.lines < 5 || col < 10) {
1184 sleep_for_duration(interval);
1185 continue;
1186 }
1187 if (col > LINE_BUF_SIZE - 2)
1188 col = LINE_BUF_SIZE - 2;
1189 }
1190
1191
1192 ntop = 0;
1193 while ((p = procps_scan(p, scan_mask)) != NULL) {
1194 int n;
1195
1196 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1197 n = ntop;
1198 top = xrealloc_vector(top, 6, ntop++);
1199 top[n].pid = p->pid;
1200 top[n].ppid = p->ppid;
1201 top[n].vsz = p->vsz;
1202#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1203 top[n].ticks = p->stime + p->utime;
1204#endif
1205 top[n].uid = p->uid;
1206 strcpy(top[n].state, p->state);
1207 strcpy(top[n].comm, p->comm);
1208#if ENABLE_FEATURE_TOP_SMP_PROCESS
1209 top[n].last_seen_on_cpu = p->last_seen_on_cpu;
1210#endif
1211 }
1212#if ENABLE_FEATURE_TOPMEM
1213 else {
1214 if (!(p->smaps.mapped_ro | p->smaps.mapped_rw))
1215 continue;
1216 n = ntop;
1217
1218 top = xrealloc_vector(topmem, 6, ntop++);
1219 strcpy(topmem[n].comm, p->comm);
1220 topmem[n].pid = p->pid;
1221 topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro;
1222 topmem[n].vszrw = p->smaps.mapped_rw;
1223 topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty;
1224 topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh;
1225 topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty;
1226 topmem[n].dirty_sh = p->smaps.shared_dirty;
1227 topmem[n].stack = p->smaps.stack;
1228 }
1229#endif
1230 }
1231 if (ntop == 0) {
1232 bb_error_msg("no process info in /proc");
1233 break;
1234 }
1235
1236 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1237#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1238 if (!prev_hist_count) {
1239 do_stats();
1240 usleep(100000);
1241 clearmems();
1242 continue;
1243 }
1244 do_stats();
1245
1246 qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
1247#else
1248 qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
1249#endif
1250 }
1251#if ENABLE_FEATURE_TOPMEM
1252 else {
1253 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1254 }
1255#endif
1256 IF_FEATURE_TOP_INTERACTIVE(display:)
1257 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1258 display_process_list(G.lines, col);
1259 }
1260#if ENABLE_FEATURE_TOPMEM
1261 else {
1262 display_topmem_process_list(G.lines, col);
1263 }
1264#endif
1265 if (iterations >= 0 && !--iterations)
1266 break;
1267#if !ENABLE_FEATURE_TOP_INTERACTIVE
1268 clearmems();
1269 sleep_for_duration(interval);
1270#else
1271 new_mask = handle_input(scan_mask, interval);
1272 if (new_mask == NO_RESCAN_MASK)
1273 goto display;
1274 scan_mask = new_mask;
1275 clearmems();
1276#endif
1277 }
1278
1279 bb_putchar('\n');
1280#if ENABLE_FEATURE_TOP_INTERACTIVE
1281 reset_term();
1282#endif
1283 if (ENABLE_FEATURE_CLEAN_UP) {
1284 clearmems();
1285#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1286 free(prev_hist);
1287#endif
1288 }
1289 return EXIT_SUCCESS;
1290}
1291