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