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_stdout(line_buf);
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# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
983 free(prev_hist);
984 prev_hist = NULL;
985 prev_hist_count = 0;
986# endif
987 continue;
988 }
989# endif
990# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
991 if (c == 'p') {
992 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
993 sort_function[0] = pcpu_sort;
994 sort_function[1] = mem_sort;
995 sort_function[2] = time_sort;
996 continue;
997 }
998 if (c == 't') {
999 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1000 sort_function[0] = time_sort;
1001 sort_function[1] = mem_sort;
1002 sort_function[2] = pcpu_sort;
1003 continue;
1004 }
1005# if ENABLE_FEATURE_TOPMEM
1006 if (c == 's') {
1007 scan_mask = TOPMEM_MASK;
1008 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1009 free(prev_hist);
1010 prev_hist = NULL;
1011 prev_hist_count = 0;
1012 continue;
1013 }
1014# endif
1015 if (c == 'r') {
1016 inverted ^= 1;
1017 continue;
1018 }
1019# if ENABLE_FEATURE_TOP_SMP_CPU
1020
1021 if (c == 'c' || c == '1') {
1022
1023 if (smp_cpu_info) {
1024 free(cpu_prev_jif);
1025 free(cpu_jif);
1026 cpu_jif = &cur_jif;
1027 cpu_prev_jif = &prev_jif;
1028 } else {
1029
1030 cpu_jif = cpu_prev_jif = NULL;
1031 }
1032 num_cpus = 0;
1033 smp_cpu_info = !smp_cpu_info;
1034 get_jiffy_counts();
1035 continue;
1036 }
1037# endif
1038# endif
1039 break;
1040 }
1041
1042 return scan_mask;
1043}
1044#endif
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
1096
1097
1098
1099
1100
1101int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1102int top_main(int argc UNUSED_PARAM, char **argv)
1103{
1104 duration_t interval;
1105 int iterations;
1106 unsigned col;
1107 char *str_interval, *str_iterations;
1108 unsigned scan_mask = TOP_MASK;
1109
1110 INIT_G();
1111
1112 interval = 5;
1113 iterations = 0;
1114#if ENABLE_FEATURE_TOP_SMP_CPU
1115
1116
1117 cpu_jif = &cur_jif;
1118 cpu_prev_jif = &prev_jif;
1119#endif
1120
1121
1122 make_all_argv_opts(argv);
1123 col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations);
1124
1125#if ENABLE_FEATURE_TOPMEM
1126 if (col & OPT_m)
1127 scan_mask = TOPMEM_MASK;
1128#endif
1129 if (col & OPT_d) {
1130
1131 if (str_interval[0] == '-')
1132 str_interval++;
1133 interval = parse_duration_str(str_interval);
1134
1135 if (interval > INT_MAX / 1000)
1136 interval = INT_MAX / 1000;
1137 }
1138 if (col & OPT_n) {
1139 if (str_iterations[0] == '-')
1140 str_iterations++;
1141 iterations = xatou(str_iterations);
1142 }
1143#if ENABLE_FEATURE_SHOW_THREADS
1144 if (col & OPT_H) {
1145 scan_mask |= PSSCAN_TASKS;
1146 }
1147#endif
1148
1149
1150 xchdir("/proc");
1151
1152#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1153 sort_function[0] = pcpu_sort;
1154 sort_function[1] = mem_sort;
1155 sort_function[2] = time_sort;
1156#else
1157 sort_function[0] = mem_sort;
1158#endif
1159
1160 if (OPT_BATCH_MODE) {
1161 option_mask32 |= OPT_EOF;
1162 }
1163#if ENABLE_FEATURE_TOP_INTERACTIVE
1164 else {
1165
1166 set_termios_to_raw(STDIN_FILENO, &initial_settings, TERMIOS_CLEAR_ISIG);
1167 die_func = reset_term;
1168 }
1169
1170 bb_signals(BB_FATAL_SIGS, sig_catcher);
1171
1172
1173 scan_mask = handle_input(scan_mask, 0);
1174#endif
1175
1176 while (scan_mask != EXIT_MASK) {
1177 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask;)
1178 procps_status_t *p = NULL;
1179
1180 if (OPT_BATCH_MODE) {
1181 G.lines = INT_MAX;
1182 col = LINE_BUF_SIZE - 2;
1183 } else {
1184 G.lines = 24;
1185 col = 79;
1186
1187 get_terminal_width_height(STDOUT_FILENO, &col, &G.lines);
1188 if (G.lines < 5 || col < 10) {
1189 sleep_for_duration(interval);
1190 continue;
1191 }
1192 if (col > LINE_BUF_SIZE - 2)
1193 col = LINE_BUF_SIZE - 2;
1194 }
1195
1196
1197 ntop = 0;
1198 while ((p = procps_scan(p, scan_mask)) != NULL) {
1199 int n;
1200
1201 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1202 n = ntop;
1203 top = xrealloc_vector(top, 6, ntop++);
1204 top[n].pid = p->pid;
1205 top[n].ppid = p->ppid;
1206 top[n].vsz = p->vsz;
1207#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1208 top[n].ticks = p->stime + p->utime;
1209#endif
1210 top[n].uid = p->uid;
1211 strcpy(top[n].state, p->state);
1212 strcpy(top[n].comm, p->comm);
1213#if ENABLE_FEATURE_TOP_SMP_PROCESS
1214 top[n].last_seen_on_cpu = p->last_seen_on_cpu;
1215#endif
1216 }
1217#if ENABLE_FEATURE_TOPMEM
1218 else {
1219 if (!(p->smaps.mapped_ro | p->smaps.mapped_rw))
1220 continue;
1221 n = ntop;
1222
1223 top = xrealloc_vector(topmem, 6, ntop++);
1224 strcpy(topmem[n].comm, p->comm);
1225 topmem[n].pid = p->pid;
1226 topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro;
1227 topmem[n].vszrw = p->smaps.mapped_rw;
1228 topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty;
1229 topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh;
1230 topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty;
1231 topmem[n].dirty_sh = p->smaps.shared_dirty;
1232 topmem[n].stack = p->smaps.stack;
1233 }
1234#endif
1235 }
1236 if (ntop == 0) {
1237 bb_simple_error_msg("no process info in /proc");
1238 break;
1239 }
1240
1241 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1242#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1243 if (!prev_hist_count) {
1244 do_stats();
1245 usleep(100000);
1246 clearmems();
1247 continue;
1248 }
1249 do_stats();
1250
1251 qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
1252#else
1253 qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
1254#endif
1255 }
1256#if ENABLE_FEATURE_TOPMEM
1257 else {
1258 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1259 }
1260#endif
1261 IF_FEATURE_TOP_INTERACTIVE(display:)
1262 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1263 display_process_list(G.lines, col);
1264 }
1265#if ENABLE_FEATURE_TOPMEM
1266 else {
1267 display_topmem_process_list(G.lines, col);
1268 }
1269#endif
1270 if (iterations >= 0 && !--iterations)
1271 break;
1272#if !ENABLE_FEATURE_TOP_INTERACTIVE
1273 clearmems();
1274 sleep_for_duration(interval);
1275#else
1276 new_mask = handle_input(scan_mask, interval);
1277 if (new_mask == NO_RESCAN_MASK)
1278 goto display;
1279 scan_mask = new_mask;
1280 clearmems();
1281#endif
1282 }
1283
1284 bb_putchar('\n');
1285#if ENABLE_FEATURE_TOP_INTERACTIVE
1286 reset_term();
1287#endif
1288 if (ENABLE_FEATURE_CLEAN_UP) {
1289 clearmems();
1290#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1291 free(prev_hist);
1292#endif
1293 }
1294 return EXIT_SUCCESS;
1295}
1296