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