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#if ENABLE_FEATURE_TOP_DECIMALS
623# define UPSCALE 1000
624typedef struct { unsigned quot, rem; } bb_div_t;
625
626
627
628
629
630# define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 }
631# define SHOW_STAT(name) name.quot, '0'+name.rem
632# define FMT "%3u.%c"
633#else
634# define UPSCALE 100
635# define CALC_STAT(name, val) unsigned name = (val)
636# define SHOW_STAT(name) name
637# define FMT "%4u%%"
638#endif
639
640
641 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width,
642 " PID PPID USER STAT VSZ %VSZ"
643 IF_FEATURE_TOP_SMP_PROCESS(" CPU")
644 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
645 " COMMAND");
646 lines_rem--;
647
648
649
650
651 pmem_shift = BITS_PER_INT-11;
652 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
653
654 while (pmem_scale >= 512) {
655 pmem_scale /= 4;
656 pmem_shift -= 2;
657 }
658 pmem_half = (1U << pmem_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
659#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
660 busy_jifs = cur_jif.busy - prev_jif.busy;
661
662
663 if (total_pcpu < busy_jifs) total_pcpu = busy_jifs;
664
665
666
667
668
669
670
671
672
673 pcpu_shift = 6;
674 pcpu_scale = UPSCALE*64 * (uint16_t)busy_jifs;
675 if (pcpu_scale == 0)
676 pcpu_scale = 1;
677 while (pcpu_scale < (1U << (BITS_PER_INT-2))) {
678 pcpu_scale *= 4;
679 pcpu_shift += 2;
680 }
681 tmp_unsigned = (uint16_t)(cur_jif.total - prev_jif.total) * total_pcpu;
682 if (tmp_unsigned != 0)
683 pcpu_scale /= tmp_unsigned;
684
685 while (pcpu_scale >= 1024) {
686 pcpu_scale /= 4;
687 pcpu_shift -= 2;
688 }
689 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
690
691#endif
692
693
694 scr_width += 2;
695 if (lines_rem > ntop - G_scroll_ofs)
696 lines_rem = ntop - G_scroll_ofs;
697 s = top + G_scroll_ofs;
698 while (--lines_rem >= 0) {
699 int n;
700 char *ppu;
701 char ppubuf[sizeof(int)*3 * 2 + 12];
702 char vsz_str_buf[8];
703 unsigned col;
704
705 CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
706#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
707 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
708#endif
709
710 smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy");
711
712 n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid));
713 ppu = ppubuf;
714 if (n != 6+6+8) {
715
716
717
718
719
720
721 char *p, *pp;
722 if (*ppu == ' ') {
723 do {
724 ppu++, n--;
725 if (n == 6+6+8)
726 goto shortened;
727 } while (*ppu == ' ');
728 }
729 pp = p = skip_non_whitespace(ppu) + 1;
730 if (*p == ' ') {
731 do
732 p++, n--;
733 while (n != 6+6+8 && *p == ' ');
734 overlapping_strcpy(pp, p);
735 }
736 ppu[6+6+8] = '\0';
737 }
738 shortened:
739 col = snprintf(line_buf, scr_width,
740 "\n" "%s %s %.5s" FMT
741 IF_FEATURE_TOP_SMP_PROCESS(" %3d")
742 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
743 " ",
744 ppu,
745 s->state, vsz_str_buf,
746 SHOW_STAT(pmem)
747 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
748 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
749 );
750 if ((int)(scr_width - col) > 1)
751 read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm);
752 fputs_stdout(line_buf);
753
754
755 s++;
756 }
757
758 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
759 fflush_all();
760}
761#undef UPSCALE
762#undef SHOW_STAT
763#undef CALC_STAT
764#undef FMT
765
766static void clearmems(void)
767{
768 clear_username_cache();
769 free(top);
770 top = NULL;
771}
772
773#if ENABLE_FEATURE_TOP_INTERACTIVE
774static void reset_term(void)
775{
776 if (!OPT_BATCH_MODE)
777 tcsetattr_stdin_TCSANOW(&initial_settings);
778}
779
780static void sig_catcher(int sig)
781{
782 reset_term();
783 kill_myself_with_sig(sig);
784}
785#endif
786
787
788
789
790
791typedef unsigned long mem_t;
792
793typedef struct topmem_status_t {
794 unsigned pid;
795 char comm[COMM_LEN];
796
797 mem_t vsz ;
798 mem_t vszrw ;
799 mem_t rss ;
800 mem_t rss_sh ;
801 mem_t dirty ;
802 mem_t dirty_sh;
803 mem_t stack ;
804} topmem_status_t;
805
806enum { NUM_SORT_FIELD = 7 };
807
808#define topmem ((topmem_status_t*)top)
809
810#if ENABLE_FEATURE_TOPMEM
811
812static int topmem_sort(char *a, char *b)
813{
814 int n;
815 mem_t l, r;
816
817 n = offsetof(topmem_status_t, vsz) + (sort_field * sizeof(mem_t));
818 l = *(mem_t*)(a + n);
819 r = *(mem_t*)(b + n);
820 if (l == r) {
821 l = ((topmem_status_t*)a)->dirty;
822 r = ((topmem_status_t*)b)->dirty;
823 }
824
825
826 n = (l > r) ? -1 : (l != r);
827 return inverted ? -n : n;
828}
829
830
831static void display_topmem_header(int scr_width, int *lines_rem_p)
832{
833 unsigned long meminfo[MI_MAX];
834
835 parse_meminfo(meminfo);
836
837 snprintf(line_buf, LINE_BUF_SIZE,
838 "Mem total:%lu anon:%lu map:%lu free:%lu",
839 meminfo[MI_MEMTOTAL],
840 meminfo[MI_ANONPAGES],
841 meminfo[MI_MAPPED],
842 meminfo[MI_MEMFREE]);
843 printf(OPT_BATCH_MODE ? "%.*s\n" : ESC"[H" ESC"[J" "%.*s\n", scr_width, line_buf);
844
845 snprintf(line_buf, LINE_BUF_SIZE,
846 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu",
847 meminfo[MI_SLAB],
848 meminfo[MI_BUFFERS],
849 meminfo[MI_CACHED],
850 meminfo[MI_DIRTY],
851 meminfo[MI_WRITEBACK]);
852 printf("%.*s\n", scr_width, line_buf);
853
854 snprintf(line_buf, LINE_BUF_SIZE,
855 "Swap total:%lu free:%lu",
856 meminfo[MI_SWAPTOTAL],
857 meminfo[MI_SWAPFREE]);
858 printf("%.*s\n", scr_width, line_buf);
859
860 (*lines_rem_p) -= 3;
861}
862
863
864static void ulltoa5_and_space(unsigned long long ul, char buf[6])
865{
866 smart_ulltoa5(ul, buf, " mgtpezy")[0] = ' ';
867}
868static void ulltoa4_and_space(unsigned long long ul, char buf[5])
869{
870 smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' ';
871}
872
873static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width)
874{
875#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK"
876#define MIN_WIDTH sizeof(HDR_STR)
877 const topmem_status_t *s = topmem + G_scroll_ofs;
878 char *cp, ch;
879
880 display_topmem_header(scr_width, &lines_rem);
881
882 strcpy(line_buf, HDR_STR " COMMAND");
883
884 cp = &line_buf[5 + sort_field * 6];
885 ch = "^_"[inverted];
886 cp[6] = ch;
887 do *cp++ = ch; while (*cp == ' ');
888
889 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width, line_buf);
890 lines_rem--;
891
892 if (lines_rem > ntop - G_scroll_ofs)
893 lines_rem = ntop - G_scroll_ofs;
894 while (--lines_rem >= 0) {
895
896 int n = sprintf(line_buf, "%5u ", s->pid);
897 if (n > 7) {
898
899 ulltoa4_and_space(s->vsz , &line_buf[8]);
900 ulltoa4_and_space(s->vszrw, &line_buf[8+5]);
901
902 } else {
903 if (n == 7)
904 ulltoa4_and_space(s->vsz, &line_buf[7]);
905
906 else
907 ulltoa5_and_space(s->vsz, &line_buf[6]);
908 ulltoa5_and_space(s->vszrw, &line_buf[2*6]);
909 }
910 ulltoa5_and_space(s->rss , &line_buf[3*6]);
911 ulltoa5_and_space(s->rss_sh , &line_buf[4*6]);
912 ulltoa5_and_space(s->dirty , &line_buf[5*6]);
913 ulltoa5_and_space(s->dirty_sh, &line_buf[6*6]);
914 ulltoa5_and_space(s->stack , &line_buf[7*6]);
915 line_buf[8*6] = '\0';
916 if (scr_width > (int)MIN_WIDTH) {
917 read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
918 }
919 printf("\n""%.*s", scr_width, line_buf);
920 s++;
921 }
922 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
923 fflush_all();
924#undef HDR_STR
925#undef MIN_WIDTH
926}
927
928#else
929void display_topmem_process_list(int lines_rem, int scr_width);
930int topmem_sort(char *a, char *b);
931#endif
932
933
934
935
936
937enum {
938 TOP_MASK = 0
939 | PSSCAN_PID
940 | PSSCAN_PPID
941 | PSSCAN_VSZ
942 | PSSCAN_STIME
943 | PSSCAN_UTIME
944 | PSSCAN_STATE
945 | PSSCAN_COMM
946 | PSSCAN_CPU
947 | PSSCAN_UIDGID,
948 TOPMEM_MASK = 0
949 | PSSCAN_PID
950 | PSSCAN_SMAPS
951 | PSSCAN_COMM,
952 EXIT_MASK = 0,
953 NO_RESCAN_MASK = (unsigned)-1,
954};
955
956#if ENABLE_FEATURE_TOP_INTERACTIVE
957static unsigned handle_input(unsigned scan_mask, duration_t interval)
958{
959 if (option_mask32 & OPT_EOF) {
960
961 sleep_for_duration(interval);
962 return scan_mask;
963 }
964
965 while (1) {
966 int32_t c;
967
968 c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
969 if (c == -1 && errno != EAGAIN) {
970
971 option_mask32 |= OPT_EOF;
972 break;
973 }
974 interval = 0;
975
976 if (c == initial_settings.c_cc[VINTR])
977 return EXIT_MASK;
978 if (c == initial_settings.c_cc[VEOF])
979 return EXIT_MASK;
980
981 if (c == KEYCODE_UP) {
982 G_scroll_ofs--;
983 goto normalize_ofs;
984 }
985 if (c == KEYCODE_DOWN) {
986 G_scroll_ofs++;
987 goto normalize_ofs;
988 }
989 if (c == KEYCODE_HOME) {
990 G_scroll_ofs = 0;
991 goto normalize_ofs;
992 }
993 if (c == KEYCODE_END) {
994 G_scroll_ofs = ntop - G.lines / 2;
995 goto normalize_ofs;
996 }
997 if (c == KEYCODE_PAGEUP) {
998 G_scroll_ofs -= G.lines / 2;
999 goto normalize_ofs;
1000 }
1001 if (c == KEYCODE_PAGEDOWN) {
1002 G_scroll_ofs += G.lines / 2;
1003 normalize_ofs:
1004 if (G_scroll_ofs >= ntop)
1005 G_scroll_ofs = ntop - 1;
1006 if (G_scroll_ofs < 0)
1007 G_scroll_ofs = 0;
1008 return NO_RESCAN_MASK;
1009 }
1010
1011 c |= 0x20;
1012 if (c == 'q')
1013 return EXIT_MASK;
1014
1015 if (c == 'n') {
1016 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1017 sort_function[0] = pid_sort;
1018 continue;
1019 }
1020 if (c == 'm') {
1021 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1022 sort_function[0] = mem_sort;
1023# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1024 sort_function[1] = pcpu_sort;
1025 sort_function[2] = time_sort;
1026# endif
1027 continue;
1028 }
1029# if ENABLE_FEATURE_SHOW_THREADS
1030 if (c == 'h'
1031 IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
1032 ) {
1033 scan_mask ^= PSSCAN_TASKS;
1034# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1035 free(prev_hist);
1036 prev_hist = NULL;
1037 prev_hist_count = 0;
1038# endif
1039 continue;
1040 }
1041# endif
1042# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1043 if (c == 'p') {
1044 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1045 sort_function[0] = pcpu_sort;
1046 sort_function[1] = mem_sort;
1047 sort_function[2] = time_sort;
1048 continue;
1049 }
1050 if (c == 't') {
1051 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1052 sort_function[0] = time_sort;
1053 sort_function[1] = mem_sort;
1054 sort_function[2] = pcpu_sort;
1055 continue;
1056 }
1057# if ENABLE_FEATURE_TOPMEM
1058 if (c == 's') {
1059 scan_mask = TOPMEM_MASK;
1060 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1061 free(prev_hist);
1062 prev_hist = NULL;
1063 prev_hist_count = 0;
1064 continue;
1065 }
1066# endif
1067 if (c == 'r') {
1068 inverted ^= 1;
1069 continue;
1070 }
1071# if ENABLE_FEATURE_TOP_SMP_CPU
1072
1073 if (c == 'c' || c == '1') {
1074
1075 if (smp_cpu_info) {
1076 free(cpu_prev_jif);
1077 free(cpu_jif);
1078 cpu_jif = &cur_jif;
1079 cpu_prev_jif = &prev_jif;
1080 } else {
1081
1082 cpu_jif = cpu_prev_jif = NULL;
1083 }
1084 num_cpus = 0;
1085 smp_cpu_info = !smp_cpu_info;
1086 get_jiffy_counts();
1087 continue;
1088 }
1089# endif
1090# endif
1091 break;
1092 }
1093
1094 return scan_mask;
1095}
1096#endif
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1154int top_main(int argc UNUSED_PARAM, char **argv)
1155{
1156 duration_t interval;
1157 int iterations;
1158 unsigned col;
1159 char *str_interval, *str_iterations;
1160 unsigned scan_mask = TOP_MASK;
1161
1162 INIT_G();
1163
1164 interval = 5;
1165 iterations = 0;
1166#if ENABLE_FEATURE_TOP_SMP_CPU
1167
1168
1169 cpu_jif = &cur_jif;
1170 cpu_prev_jif = &prev_jif;
1171#endif
1172
1173
1174 make_all_argv_opts(argv);
1175 col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations);
1176
1177#if ENABLE_FEATURE_TOPMEM
1178 if (col & OPT_m)
1179 scan_mask = TOPMEM_MASK;
1180#endif
1181 if (col & OPT_d) {
1182
1183 if (str_interval[0] == '-')
1184 str_interval++;
1185 interval = parse_duration_str(str_interval);
1186
1187 if (interval > INT_MAX / 1000)
1188 interval = INT_MAX / 1000;
1189 }
1190 if (col & OPT_n) {
1191 if (str_iterations[0] == '-')
1192 str_iterations++;
1193 iterations = xatou(str_iterations);
1194 }
1195#if ENABLE_FEATURE_SHOW_THREADS
1196 if (col & OPT_H) {
1197 scan_mask |= PSSCAN_TASKS;
1198 }
1199#endif
1200
1201
1202 xchdir("/proc");
1203
1204#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1205 sort_function[0] = pcpu_sort;
1206 sort_function[1] = mem_sort;
1207 sort_function[2] = time_sort;
1208#else
1209 sort_function[0] = mem_sort;
1210#endif
1211
1212 if (OPT_BATCH_MODE) {
1213 option_mask32 |= OPT_EOF;
1214 }
1215#if ENABLE_FEATURE_TOP_INTERACTIVE
1216 else {
1217
1218 set_termios_to_raw(STDIN_FILENO, &initial_settings, TERMIOS_CLEAR_ISIG);
1219 die_func = reset_term;
1220 }
1221
1222 bb_signals(BB_FATAL_SIGS, sig_catcher);
1223
1224
1225 scan_mask = handle_input(scan_mask, 0);
1226#endif
1227
1228 while (scan_mask != EXIT_MASK) {
1229 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask;)
1230 procps_status_t *p = NULL;
1231
1232 if (OPT_BATCH_MODE) {
1233 G.lines = INT_MAX;
1234 col = LINE_BUF_SIZE - 2;
1235 } else {
1236 G.lines = 24;
1237 col = 79;
1238
1239 get_terminal_width_height(STDOUT_FILENO, &col, &G.lines);
1240 if (G.lines < 5 || col < 10) {
1241 sleep_for_duration(interval);
1242 continue;
1243 }
1244 if (col > LINE_BUF_SIZE - 2)
1245 col = LINE_BUF_SIZE - 2;
1246 }
1247
1248
1249 ntop = 0;
1250 while ((p = procps_scan(p, scan_mask)) != NULL) {
1251 int n;
1252
1253 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1254 n = ntop;
1255 top = xrealloc_vector(top, 6, ntop++);
1256 top[n].pid = p->pid;
1257 top[n].ppid = p->ppid;
1258 top[n].vsz = p->vsz;
1259#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1260 top[n].ticks = p->stime + p->utime;
1261#endif
1262 top[n].uid = p->uid;
1263 strcpy(top[n].state, p->state);
1264 strcpy(top[n].comm, p->comm);
1265#if ENABLE_FEATURE_TOP_SMP_PROCESS
1266 top[n].last_seen_on_cpu = p->last_seen_on_cpu;
1267#endif
1268 }
1269#if ENABLE_FEATURE_TOPMEM
1270 else {
1271 if (!(p->smaps.mapped_ro | p->smaps.mapped_rw))
1272 continue;
1273 n = ntop;
1274
1275 top = xrealloc_vector(topmem, 6, ntop++);
1276 strcpy(topmem[n].comm, p->comm);
1277 topmem[n].pid = p->pid;
1278 topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro;
1279 topmem[n].vszrw = p->smaps.mapped_rw;
1280 topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty;
1281 topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh;
1282 topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty;
1283 topmem[n].dirty_sh = p->smaps.shared_dirty;
1284 topmem[n].stack = p->smaps.stack;
1285 }
1286#endif
1287 }
1288 if (ntop == 0) {
1289 bb_simple_error_msg("no process info in /proc");
1290 break;
1291 }
1292
1293 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1294#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1295 if (!prev_hist_count) {
1296 do_stats();
1297 usleep(100000);
1298 clearmems();
1299 continue;
1300 }
1301 do_stats();
1302
1303 qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
1304#else
1305 qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
1306#endif
1307 }
1308#if ENABLE_FEATURE_TOPMEM
1309 else {
1310 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1311 }
1312#endif
1313 IF_FEATURE_TOP_INTERACTIVE(display:)
1314 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1315 display_process_list(G.lines, col);
1316 }
1317#if ENABLE_FEATURE_TOPMEM
1318 else {
1319 display_topmem_process_list(G.lines, col);
1320 }
1321#endif
1322 if (iterations >= 0 && !--iterations)
1323 break;
1324#if !ENABLE_FEATURE_TOP_INTERACTIVE
1325 clearmems();
1326 sleep_for_duration(interval);
1327#else
1328 new_mask = handle_input(scan_mask, interval);
1329 if (new_mask == NO_RESCAN_MASK)
1330 goto display;
1331 scan_mask = new_mask;
1332 clearmems();
1333#endif
1334 }
1335
1336 bb_putchar('\n');
1337#if ENABLE_FEATURE_TOP_INTERACTIVE
1338 reset_term();
1339#endif
1340 if (ENABLE_FEATURE_CLEAN_UP) {
1341 clearmems();
1342#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1343 free(prev_hist);
1344#endif
1345 }
1346 return EXIT_SUCCESS;
1347}
1348