aboutsummaryrefslogtreecommitdiff
path: root/procps
diff options
context:
space:
mode:
Diffstat (limited to 'procps')
-rw-r--r--procps/free.c9
-rw-r--r--procps/iostat.c2
-rw-r--r--procps/kill.c2
-rw-r--r--procps/mpstat.c2
-rw-r--r--procps/pgrep.c55
-rw-r--r--procps/pmap.c126
-rw-r--r--procps/ps.c16
-rw-r--r--procps/smemcap.c1
-rw-r--r--procps/top.c351
-rw-r--r--procps/watch.c7
10 files changed, 405 insertions, 166 deletions
diff --git a/procps/free.c b/procps/free.c
index d0c849b79..90b4af702 100644
--- a/procps/free.c
+++ b/procps/free.c
@@ -77,6 +77,7 @@ static const char *scale(struct globals *g, unsigned long d)
77 return make_human_readable_str(d, g->mem_unit, G_unit); 77 return make_human_readable_str(d, g->mem_unit, G_unit);
78} 78}
79 79
80#if !ENABLE_PLATFORM_MINGW32
80/* NOINLINE reduces main() stack usage, which makes code smaller (on x86 at least) */ 81/* NOINLINE reduces main() stack usage, which makes code smaller (on x86 at least) */
81static NOINLINE unsigned int parse_meminfo(struct globals *g) 82static NOINLINE unsigned int parse_meminfo(struct globals *g)
82{ 83{
@@ -103,6 +104,14 @@ static NOINLINE unsigned int parse_meminfo(struct globals *g)
103 104
104 return seen_cached_and_available_and_reclaimable == 0; 105 return seen_cached_and_available_and_reclaimable == 0;
105} 106}
107#else
108static unsigned int parse_meminfo(struct globals *g)
109{
110 g->cached_kb = g->available_kb = g->reclaimable_kb = 0;
111
112 return 1;
113}
114#endif
106 115
107int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 116int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
108int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM)) 117int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
diff --git a/procps/iostat.c b/procps/iostat.c
index d3157757f..2feb02456 100644
--- a/procps/iostat.c
+++ b/procps/iostat.c
@@ -28,7 +28,7 @@
28#if 1 28#if 1
29typedef unsigned long long cputime_t; 29typedef unsigned long long cputime_t;
30typedef long long icputime_t; 30typedef long long icputime_t;
31# define FMT_DATA "ll" 31# define FMT_DATA LL_FMT
32# define CPUTIME_MAX (~0ULL) 32# define CPUTIME_MAX (~0ULL)
33#else 33#else
34typedef unsigned long cputime_t; 34typedef unsigned long cputime_t;
diff --git a/procps/kill.c b/procps/kill.c
index d4be18dd8..6be11296e 100644
--- a/procps/kill.c
+++ b/procps/kill.c
@@ -207,6 +207,7 @@ int kill_main(int argc UNUSED_PARAM, char **argv)
207 do_it_now: 207 do_it_now:
208 pid = getpid(); 208 pid = getpid();
209 209
210#if ENABLE_KILLALL5
210 if (is_killall5) { 211 if (is_killall5) {
211 pid_t sid; 212 pid_t sid;
212 procps_status_t* p = NULL; 213 procps_status_t* p = NULL;
@@ -264,6 +265,7 @@ int kill_main(int argc UNUSED_PARAM, char **argv)
264 kill(-1, SIGCONT); 265 kill(-1, SIGCONT);
265 return errors; 266 return errors;
266 } 267 }
268#endif
267 269
268#if ENABLE_KILL || ENABLE_KILLALL 270#if ENABLE_KILL || ENABLE_KILLALL
269 /* Pid or name is required for kill/killall */ 271 /* Pid or name is required for kill/killall */
diff --git a/procps/mpstat.c b/procps/mpstat.c
index 795b4ccb7..d8158ef8f 100644
--- a/procps/mpstat.c
+++ b/procps/mpstat.c
@@ -49,7 +49,7 @@
49#if 1 49#if 1
50typedef unsigned long long data_t; 50typedef unsigned long long data_t;
51typedef long long idata_t; 51typedef long long idata_t;
52#define FMT_DATA "ll" 52#define FMT_DATA LL_FMT
53#define DATA_MAX ULLONG_MAX 53#define DATA_MAX ULLONG_MAX
54#else 54#else
55typedef unsigned long data_t; 55typedef unsigned long data_t;
diff --git a/procps/pgrep.c b/procps/pgrep.c
index 04ae92a67..9773a72c4 100644
--- a/procps/pgrep.c
+++ b/procps/pgrep.c
@@ -30,31 +30,49 @@
30//kbuild:lib-$(CONFIG_PKILL) += pgrep.o 30//kbuild:lib-$(CONFIG_PKILL) += pgrep.o
31 31
32//usage:#define pgrep_trivial_usage 32//usage:#define pgrep_trivial_usage
33//usage: IF_NOT_PLATFORM_MINGW32(
33//usage: "[-flanovx] [-s SID|-P PPID|PATTERN]" 34//usage: "[-flanovx] [-s SID|-P PPID|PATTERN]"
35//usage: )
36//usage: IF_PLATFORM_MINGW32(
37//usage: "[-lvx] [-P PPID|PATTERN]"
38//usage: )
34//usage:#define pgrep_full_usage "\n\n" 39//usage:#define pgrep_full_usage "\n\n"
35//usage: "Display process(es) selected by regex PATTERN\n" 40//usage: "Display process(es) selected by regex PATTERN\n"
36//usage: "\n -l Show command name too" 41//usage: "\n -l Show command name too"
42//usage: IF_NOT_PLATFORM_MINGW32(
37//usage: "\n -a Show command line too" 43//usage: "\n -a Show command line too"
38//usage: "\n -f Match against entire command line" 44//usage: "\n -f Match against entire command line"
39//usage: "\n -n Show the newest process only" 45//usage: "\n -n Show the newest process only"
40//usage: "\n -o Show the oldest process only" 46//usage: "\n -o Show the oldest process only"
47//usage: )
41//usage: "\n -v Negate the match" 48//usage: "\n -v Negate the match"
42//usage: "\n -x Match whole name (not substring)" 49//usage: "\n -x Match whole name (not substring)"
50//usage: IF_NOT_PLATFORM_MINGW32(
43//usage: "\n -s Match session ID (0 for current)" 51//usage: "\n -s Match session ID (0 for current)"
52//usage: )
44//usage: "\n -P Match parent process ID" 53//usage: "\n -P Match parent process ID"
45//usage: 54//usage:
46//usage:#define pkill_trivial_usage 55//usage:#define pkill_trivial_usage
56//usage: IF_NOT_PLATFORM_MINGW32(
47//usage: "[-l|-SIGNAL] [-xfvnoe] [-s SID|-P PPID|PATTERN]" 57//usage: "[-l|-SIGNAL] [-xfvnoe] [-s SID|-P PPID|PATTERN]"
58//usage: )
59//usage: IF_PLATFORM_MINGW32(
60//usage: "[-l|-SIGNAL] [-xve] [-P PPID|PATTERN]"
61//usage: )
48//usage:#define pkill_full_usage "\n\n" 62//usage:#define pkill_full_usage "\n\n"
49//usage: "Send signal to processes selected by regex PATTERN\n" 63//usage: "Send signal to processes selected by regex PATTERN\n"
50//usage: "\n -l List all signals" 64//usage: "\n -l List all signals"
51//usage: "\n -x Match whole name (not substring)" 65//usage: "\n -x Match whole name (not substring)"
66//usage: IF_NOT_PLATFORM_MINGW32(
52//usage: "\n -f Match against entire command line" 67//usage: "\n -f Match against entire command line"
53//usage: "\n -s SID Match session ID (0 for current)" 68//usage: "\n -s SID Match session ID (0 for current)"
69//usage: )
54//usage: "\n -P PPID Match parent process ID" 70//usage: "\n -P PPID Match parent process ID"
55//usage: "\n -v Negate the match" 71//usage: "\n -v Negate the match"
72//usage: IF_NOT_PLATFORM_MINGW32(
56//usage: "\n -n Signal the newest process only" 73//usage: "\n -n Signal the newest process only"
57//usage: "\n -o Signal the oldest process only" 74//usage: "\n -o Signal the oldest process only"
75//usage: )
58//usage: "\n -e Display name and PID of the process being killed" 76//usage: "\n -e Display name and PID of the process being killed"
59 77
60#include "libbb.h" 78#include "libbb.h"
@@ -68,25 +86,47 @@ enum {
68 /* "vlafxones:+P:+" */ 86 /* "vlafxones:+P:+" */
69 OPTBIT_V = 0, /* must be first, we need OPT_INVERT = 0/1 */ 87 OPTBIT_V = 0, /* must be first, we need OPT_INVERT = 0/1 */
70 OPTBIT_L, 88 OPTBIT_L,
89#if !ENABLE_PLATFORM_MINGW32
71 OPTBIT_A, 90 OPTBIT_A,
72 OPTBIT_F, 91 OPTBIT_F,
92#else
93#define OPTBIT_A OPTBIT_L
94#endif
73 OPTBIT_X, 95 OPTBIT_X,
96#if !ENABLE_PLATFORM_MINGW32
74 OPTBIT_O, 97 OPTBIT_O,
75 OPTBIT_N, 98 OPTBIT_N,
99#endif
76 OPTBIT_E, /* should be pkill-only, do we care? */ 100 OPTBIT_E, /* should be pkill-only, do we care? */
101#if !ENABLE_PLATFORM_MINGW32
77 OPTBIT_S, 102 OPTBIT_S,
103#endif
78 OPTBIT_P, 104 OPTBIT_P,
79}; 105};
80 106
81#define OPT_INVERT (opt & (1 << OPTBIT_V)) 107#define OPT_INVERT (opt & (1 << OPTBIT_V))
82#define OPT_LIST (opt & (1 << OPTBIT_L)) 108#define OPT_LIST (opt & (1 << OPTBIT_L))
109#if ENABLE_PLATFORM_MINGW32
110#define OPT_LISTFULL (0)
111#define OPT_FULL (0)
112#else
83#define OPT_LISTFULL (opt & (1 << OPTBIT_A)) 113#define OPT_LISTFULL (opt & (1 << OPTBIT_A))
84#define OPT_FULL (opt & (1 << OPTBIT_F)) 114#define OPT_FULL (opt & (1 << OPTBIT_F))
115#endif
85#define OPT_ANCHOR (opt & (1 << OPTBIT_X)) 116#define OPT_ANCHOR (opt & (1 << OPTBIT_X))
117#if ENABLE_PLATFORM_MINGW32
118#define OPT_FIRST (0)
119#define OPT_LAST (0)
120#else
86#define OPT_FIRST (opt & (1 << OPTBIT_O)) 121#define OPT_FIRST (opt & (1 << OPTBIT_O))
87#define OPT_LAST (opt & (1 << OPTBIT_N)) 122#define OPT_LAST (opt & (1 << OPTBIT_N))
123#endif
88#define OPT_ECHO (opt & (1 << OPTBIT_E)) 124#define OPT_ECHO (opt & (1 << OPTBIT_E))
125#if ENABLE_PLATFORM_MINGW32
126#define OPT_SID (0)
127#else
89#define OPT_SID (opt & (1 << OPTBIT_S)) 128#define OPT_SID (opt & (1 << OPTBIT_S))
129#endif
90#define OPT_PPID (opt & (1 << OPTBIT_P)) 130#define OPT_PPID (opt & (1 << OPTBIT_P))
91 131
92static void act(unsigned pid, char *cmd, int signo) 132static void act(unsigned pid, char *cmd, int signo)
@@ -112,7 +152,12 @@ int pgrep_main(int argc UNUSED_PARAM, char **argv)
112 unsigned opt; 152 unsigned opt;
113 int scan_mask; 153 int scan_mask;
114 int matched_pid; 154 int matched_pid;
155#if ENABLE_PLATFORM_MINGW32
156 const int sid2match = -1;
157 int ppid2match;
158#else
115 int sid2match, ppid2match; 159 int sid2match, ppid2match;
160#endif
116 char *cmd_last; 161 char *cmd_last;
117 procps_status_t *proc; 162 procps_status_t *proc;
118 /* These are initialized to 0 */ 163 /* These are initialized to 0 */
@@ -137,8 +182,12 @@ int pgrep_main(int argc UNUSED_PARAM, char **argv)
137 182
138 /* Parse remaining options */ 183 /* Parse remaining options */
139 ppid2match = -1; 184 ppid2match = -1;
185#if !ENABLE_PLATFORM_MINGW32
140 sid2match = -1; 186 sid2match = -1;
141 opt = getopt32(argv, "vlafxones:+P:+", &sid2match, &ppid2match); 187 opt = getopt32(argv, "vlafxones:+P:+", &sid2match, &ppid2match);
188#else
189 opt = getopt32(argv, "vlxeP:+", &ppid2match);
190#endif
142 argv += optind; 191 argv += optind;
143 192
144 if (pkill && OPT_LIST) { /* -l: print the whole signal list */ 193 if (pkill && OPT_LIST) { /* -l: print the whole signal list */
@@ -147,8 +196,10 @@ int pgrep_main(int argc UNUSED_PARAM, char **argv)
147 } 196 }
148 197
149 pid = getpid(); 198 pid = getpid();
199#if !ENABLE_PLATFORM_MINGW32
150 if (sid2match == 0) 200 if (sid2match == 0)
151 sid2match = getsid(pid); 201 sid2match = getsid(pid);
202#endif
152 203
153 scan_mask = PSSCAN_COMM | PSSCAN_ARGV0; 204 scan_mask = PSSCAN_COMM | PSSCAN_ARGV0;
154 if (OPT_FULL) 205 if (OPT_FULL)
@@ -180,6 +231,9 @@ int pgrep_main(int argc UNUSED_PARAM, char **argv)
180 } 231 }
181 232
182 cmdlen = -1; 233 cmdlen = -1;
234#if ENABLE_PLATFORM_MINGW32
235 cmd = proc->comm;
236#else
183 cmd = proc->argv0; 237 cmd = proc->argv0;
184 if (!cmd) { 238 if (!cmd) {
185 cmd = proc->comm; 239 cmd = proc->comm;
@@ -199,6 +253,7 @@ int pgrep_main(int argc UNUSED_PARAM, char **argv)
199 cmd[i] = ' '; 253 cmd[i] = ' ';
200 } 254 }
201 } 255 }
256#endif
202 257
203 if (OPT_INVERT) { 258 if (OPT_INVERT) {
204 /* "pgrep -v -P1 firefox" means "not (ppid=1 AND name=firefox)" 259 /* "pgrep -v -P1 firefox" means "not (ppid=1 AND name=firefox)"
diff --git a/procps/pmap.c b/procps/pmap.c
index 49f7688d9..3069856a4 100644
--- a/procps/pmap.c
+++ b/procps/pmap.c
@@ -29,10 +29,14 @@
29 29
30#if ULLONG_MAX == 0xffffffff 30#if ULLONG_MAX == 0xffffffff
31# define TABS "\t" 31# define TABS "\t"
32# define SIZEWIDTHx "7"
33# define SIZEWIDTH "9"
32# define AFMTLL "8" 34# define AFMTLL "8"
33# define DASHES "" 35# define DASHES ""
34#else 36#else
35# define TABS "\t\t" 37# define TABS "\t\t"
38# define SIZEWIDTHx "15"
39# define SIZEWIDTH "17"
36# define AFMTLL "16" 40# define AFMTLL "16"
37# define DASHES "--------" 41# define DASHES "--------"
38#endif 42#endif
@@ -42,49 +46,145 @@ enum {
42 OPT_q = 1 << 1, 46 OPT_q = 1 << 1,
43}; 47};
44 48
45static void print_smaprec(struct smaprec *currec, void *data) 49struct smaprec {
46{ 50 // For mixed 32/64 userspace, 32-bit pmap still needs
47 unsigned opt = (uintptr_t)data; 51 // 64-bit field here to correctly show 64-bit processes:
52 unsigned long long smap_start;
53 // Make size wider too:
54 // I've seen 1203765248 kb large "---p" mapping in a browser,
55 // this cuts close to 4 terabytes.
56 unsigned long long smap_size;
57 // (strictly speaking, other fields need to be wider too,
58 // but they are in kbytes, not bytes, and they hold sizes,
59 // not start addresses, sizes tend to be less than 4 terabytes)
60 unsigned long private_dirty;
61 unsigned long smap_pss, smap_swap;
62 char smap_mode[5];
63 char *smap_name;
64};
48 65
66// How long the filenames and command lines we want to handle?
67#define PMAP_BUFSZ 4096
68
69static void print_smaprec(struct smaprec *currec)
70{
49 printf("%0" AFMTLL "llx ", currec->smap_start); 71 printf("%0" AFMTLL "llx ", currec->smap_start);
50 72
51 if (opt & OPT_x) 73 if (option_mask32 & OPT_x)
52 printf("%7lu %7lu %7lu %7lu ", 74 printf("%7llu %7lu %7lu %7lu ",
53 currec->smap_size, 75 currec->smap_size,
54 currec->smap_pss, 76 currec->smap_pss,
55 currec->private_dirty, 77 currec->private_dirty,
56 currec->smap_swap); 78 currec->smap_swap);
57 else 79 else
58 printf("%7luK", currec->smap_size); 80 printf("%7lluK", currec->smap_size);
59 81
60 printf(" %.4s %s\n", currec->smap_mode, currec->smap_name); 82 printf(" %.4s %s\n", currec->smap_mode, currec->smap_name ? : " [ anon ]");
83}
84
85/* libbb's procps_read_smaps() looks somewhat similar,
86 * but the collected information is sufficiently different
87 * that merging them into one function is not a good idea
88 * (unless you feel masochistic today).
89 */
90static int read_smaps(pid_t pid, char buf[PMAP_BUFSZ], struct smaprec *total)
91{
92 FILE *file;
93 struct smaprec currec;
94 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
95
96 sprintf(filename, "/proc/%u/smaps", (int)pid);
97
98 file = fopen_for_read(filename);
99 if (!file)
100 return 1;
101
102 memset(&currec, 0, sizeof(currec));
103 while (fgets(buf, PMAP_BUFSZ, file)) {
104 // Each mapping datum has this form:
105 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
106 // Size: nnn kB
107 // Rss: nnn kB
108 // .....
109
110 char *tp, *p;
111
112 if (buf[0] == 'S' || buf[0] == 'P') {
113#define SCAN(S, X) \
114 if (memcmp(buf, S, sizeof(S)-1) == 0) { \
115 tp = skip_whitespace(buf + sizeof(S)-1); \
116 total->X += currec.X = fast_strtoul_10(&tp); \
117 continue; \
118 }
119 SCAN("Pss:" , smap_pss );
120 SCAN("Swap:" , smap_swap );
121 SCAN("Private_Dirty:", private_dirty);
122#undef SCAN
123 }
124 tp = strchr(buf, '-');
125 if (tp) {
126 // We reached next mapping - the line of this form:
127 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
128
129 // If we have a previous record, there's nothing more
130 // for it, print and clear currec
131 if (currec.smap_size)
132 print_smaprec(&currec);
133 free(currec.smap_name);
134 memset(&currec, 0, sizeof(currec));
135
136 *tp = ' ';
137 tp = buf;
138 currec.smap_start = fast_strtoull_16(&tp);
139 currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10;
140 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1);
141
142 // skipping "rw-s FILEOFS M:m INODE "
143 tp = skip_fields(tp, 4);
144 tp = skip_whitespace(tp); // there may be many spaces, can't just "tp++"
145 p = strchrnul(tp, '\n');
146 if (p != tp) {
147 currec.smap_name = xstrndup(tp, p - tp);
148 }
149 total->smap_size += currec.smap_size;
150 }
151 } // while (got line)
152 fclose(file);
153
154 if (currec.smap_size)
155 print_smaprec(&currec);
156 free(currec.smap_name);
157
158 return 0;
61} 159}
62 160
63static int procps_get_maps(pid_t pid, unsigned opt) 161static int procps_get_maps(pid_t pid, unsigned opt)
64{ 162{
65 struct smaprec total; 163 struct smaprec total;
66 int ret; 164 int ret;
67 char buf[256]; 165 char buf[PMAP_BUFSZ];
166
167 ret = read_cmdline(buf, sizeof(buf), pid, NULL);
168 if (ret < 0)
169 return ret;
68 170
69 read_cmdline(buf, sizeof(buf), pid, NULL);
70 printf("%u: %s\n", (int)pid, buf); 171 printf("%u: %s\n", (int)pid, buf);
71 172
72 if (!(opt & OPT_q) && (opt & OPT_x)) 173 if (!(opt & OPT_q) && (opt & OPT_x))
73 puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping"); 174 puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping");
74 175
75 memset(&total, 0, sizeof(total)); 176 memset(&total, 0, sizeof(total));
76 177 ret = read_smaps(pid, buf, &total);
77 ret = procps_read_smaps(pid, &total, print_smaprec, (void*)(uintptr_t)opt);
78 if (ret) 178 if (ret)
79 return ret; 179 return ret;
80 180
81 if (!(opt & OPT_q)) { 181 if (!(opt & OPT_q)) {
82 if (opt & OPT_x) 182 if (opt & OPT_x)
83 printf("--------" DASHES " ------ ------ ------ ------\n" 183 printf("--------" DASHES " ------ ------ ------ ------\n"
84 "total" TABS " %7lu %7lu %7lu %7lu\n", 184 "total kB %"SIZEWIDTHx"llu %7lu %7lu %7lu\n",
85 total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); 185 total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap);
86 else 186 else
87 printf("mapped: %luK\n", total.smap_size); 187 printf(" total %"SIZEWIDTH"lluK\n", total.smap_size);
88 } 188 }
89 189
90 return 0; 190 return 0;
diff --git a/procps/ps.c b/procps/ps.c
index 5b521aebd..329576eb8 100644
--- a/procps/ps.c
+++ b/procps/ps.c
@@ -120,7 +120,7 @@ enum { MAX_WIDTH = 2*1024 };
120#if ENABLE_FEATURE_PS_TIME || ENABLE_FEATURE_PS_LONG 120#if ENABLE_FEATURE_PS_TIME || ENABLE_FEATURE_PS_LONG
121static unsigned long get_uptime(void) 121static unsigned long get_uptime(void)
122{ 122{
123#ifdef __linux__ 123#if defined __linux__ || ENABLE_PLATFORM_MINGW32
124 struct sysinfo info; 124 struct sysinfo info;
125 if (sysinfo(&info) < 0) 125 if (sysinfo(&info) < 0)
126 return 0; 126 return 0;
@@ -237,10 +237,12 @@ static void func_comm(char *buf, int size, const procps_status_t *ps)
237 safe_strncpy(buf, ps->comm, size+1); 237 safe_strncpy(buf, ps->comm, size+1);
238} 238}
239 239
240#if !ENABLE_PLATFORM_MINGW32
240static void func_state(char *buf, int size, const procps_status_t *ps) 241static void func_state(char *buf, int size, const procps_status_t *ps)
241{ 242{
242 safe_strncpy(buf, ps->state, size+1); 243 safe_strncpy(buf, ps->state, size+1);
243} 244}
245#endif
244 246
245static void func_args(char *buf, int size, const procps_status_t *ps) 247static void func_args(char *buf, int size, const procps_status_t *ps)
246{ 248{
@@ -257,6 +259,7 @@ static void func_ppid(char *buf, int size, const procps_status_t *ps)
257 sprintf(buf, "%*u", size, ps->ppid); 259 sprintf(buf, "%*u", size, ps->ppid);
258} 260}
259 261
262#if !ENABLE_PLATFORM_MINGW32
260static void func_pgid(char *buf, int size, const procps_status_t *ps) 263static void func_pgid(char *buf, int size, const procps_status_t *ps)
261{ 264{
262 sprintf(buf, "%*u", size, ps->pgid); 265 sprintf(buf, "%*u", size, ps->pgid);
@@ -293,6 +296,7 @@ static void func_tty(char *buf, int size, const procps_status_t *ps)
293 if (ps->tty_major) /* tty field of "0" means "no tty" */ 296 if (ps->tty_major) /* tty field of "0" means "no tty" */
294 snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor); 297 snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor);
295} 298}
299#endif
296 300
297#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS 301#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
298static void func_rgroup(char *buf, int size, const procps_status_t *ps) 302static void func_rgroup(char *buf, int size, const procps_status_t *ps)
@@ -379,11 +383,17 @@ static const ps_out_t out_spec[] ALIGN_PTR = {
379/* Mandated by http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html: */ 383/* Mandated by http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html: */
380 { 8 , "user" ,"USER" ,func_user ,PSSCAN_UIDGID }, 384 { 8 , "user" ,"USER" ,func_user ,PSSCAN_UIDGID },
381 { 8 , "group" ,"GROUP" ,func_group ,PSSCAN_UIDGID }, 385 { 8 , "group" ,"GROUP" ,func_group ,PSSCAN_UIDGID },
386#if ENABLE_PLATFORM_MINGW32
387 { COMM_LEN , "comm" ,"COMMAND",func_comm ,PSSCAN_COMM },
388#else
382 { 16 , "comm" ,"COMMAND",func_comm ,PSSCAN_COMM }, 389 { 16 , "comm" ,"COMMAND",func_comm ,PSSCAN_COMM },
390#endif
383 { MAX_WIDTH , "args" ,"COMMAND",func_args ,PSSCAN_COMM }, 391 { MAX_WIDTH , "args" ,"COMMAND",func_args ,PSSCAN_COMM },
384 { 5 , "pid" ,"PID" ,func_pid ,PSSCAN_PID }, 392 { 5 , "pid" ,"PID" ,func_pid ,PSSCAN_PID },
385 { 5 , "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID }, 393 { 5 , "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID },
394#if !ENABLE_PLATFORM_MINGW32
386 { 5 , "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID }, 395 { 5 , "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID },
396#endif
387#if ENABLE_FEATURE_PS_TIME 397#if ENABLE_FEATURE_PS_TIME
388 { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME }, 398 { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME },
389#endif 399#endif
@@ -396,12 +406,14 @@ static const ps_out_t out_spec[] ALIGN_PTR = {
396#if ENABLE_FEATURE_PS_TIME 406#if ENABLE_FEATURE_PS_TIME
397 { 5 , "time" ,"TIME" ,func_time ,PSSCAN_STIME | PSSCAN_UTIME }, 407 { 5 , "time" ,"TIME" ,func_time ,PSSCAN_STIME | PSSCAN_UTIME },
398#endif 408#endif
409#if !ENABLE_PLATFORM_MINGW32
399 { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY }, 410 { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY },
400 { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ }, 411 { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ },
401/* Not mandated, but useful: */ 412/* Not mandated, but useful: */
402 { 5 , "sid" ,"SID" ,func_sid ,PSSCAN_SID }, 413 { 5 , "sid" ,"SID" ,func_sid ,PSSCAN_SID },
403 { 4 , "stat" ,"STAT" ,func_state ,PSSCAN_STATE }, 414 { 4 , "stat" ,"STAT" ,func_state ,PSSCAN_STATE },
404 { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS }, 415 { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS },
416#endif
405#if ENABLE_SELINUX 417#if ENABLE_SELINUX
406 { 35 , "label" ,"LABEL" ,func_label ,PSSCAN_CONTEXT }, 418 { 35 , "label" ,"LABEL" ,func_label ,PSSCAN_CONTEXT },
407#endif 419#endif
@@ -544,6 +556,8 @@ static void format_process(const procps_status_t *ps)
544#if ENABLE_SELINUX 556#if ENABLE_SELINUX
545# define SELINUX_O_PREFIX "label," 557# define SELINUX_O_PREFIX "label,"
546# define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args") 558# define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
559#elif ENABLE_PLATFORM_MINGW32
560# define DEFAULT_O_STR ("pid,ppid,user" IF_FEATURE_PS_TIME(",time,etime") ",args")
547#else 561#else
548# define DEFAULT_O_STR ("pid,user" IF_FEATURE_PS_TIME(",time") ",args") 562# define DEFAULT_O_STR ("pid,user" IF_FEATURE_PS_TIME(",time") ",args")
549#endif 563#endif
diff --git a/procps/smemcap.c b/procps/smemcap.c
index a2231788b..6839c9de0 100644
--- a/procps/smemcap.c
+++ b/procps/smemcap.c
@@ -19,6 +19,7 @@
19//kbuild:lib-$(CONFIG_SMEMCAP) += smemcap.o 19//kbuild:lib-$(CONFIG_SMEMCAP) += smemcap.o
20 20
21#include "libbb.h" 21#include "libbb.h"
22#define BB_ARCHIVE_PUBLIC
22#include "bb_archive.h" 23#include "bb_archive.h"
23 24
24struct fileblock { 25struct fileblock {
diff --git a/procps/top.c b/procps/top.c
index 09d31c673..96b3e2d4e 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -117,10 +117,15 @@
117 117
118#include "libbb.h" 118#include "libbb.h"
119 119
120#define ESC "\033" 120#define ESC "\033"
121#define HOME ESC"[H"
122#define CLREOS ESC"[J"
123#define CLREOL ESC"[K"
124#define REVERSE ESC"[7m"
125#define NORMAL ESC"[m"
121 126
122typedef struct top_status_t { 127typedef struct top_status_t {
123 unsigned long vsz; 128 unsigned long memsize;
124#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 129#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
125 unsigned long ticks; 130 unsigned long ticks;
126 unsigned pcpu; /* delta of ticks */ 131 unsigned pcpu; /* delta of ticks */
@@ -161,13 +166,16 @@ struct globals {
161 top_status_t *top; 166 top_status_t *top;
162 int ntop; 167 int ntop;
163 smallint inverted; 168 smallint inverted;
169 smallint not_first_line;
164#if ENABLE_FEATURE_TOPMEM 170#if ENABLE_FEATURE_TOPMEM
165 smallint sort_field; 171 smallint sort_field;
166#endif 172#endif
167#if ENABLE_FEATURE_TOP_SMP_CPU 173#if ENABLE_FEATURE_TOP_SMP_CPU
168 smallint smp_cpu_info; /* one/many cpu info lines? */ 174 smallint smp_cpu_info; /* one/many cpu info lines? */
169#endif 175#endif
170 unsigned lines; /* screen height */ 176 int lines_remaining;
177 unsigned lines; /* screen height */
178 unsigned scr_width; /* width, clamped <= LINE_BUF_SIZE-2 */
171#if ENABLE_FEATURE_TOP_INTERACTIVE 179#if ENABLE_FEATURE_TOP_INTERACTIVE
172 struct termios initial_settings; 180 struct termios initial_settings;
173 int scroll_ofs; 181 int scroll_ofs;
@@ -212,7 +220,6 @@ struct globals {
212#define cpu_prev_jif (G.cpu_prev_jif ) 220#define cpu_prev_jif (G.cpu_prev_jif )
213#define num_cpus (G.num_cpus ) 221#define num_cpus (G.num_cpus )
214#define total_pcpu (G.total_pcpu ) 222#define total_pcpu (G.total_pcpu )
215#define line_buf (G.line_buf )
216#define INIT_G() do { \ 223#define INIT_G() do { \
217 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 224 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
218 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \ 225 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \
@@ -241,8 +248,8 @@ static int pid_sort(top_status_t *P, top_status_t *Q)
241static int mem_sort(top_status_t *P, top_status_t *Q) 248static int mem_sort(top_status_t *P, top_status_t *Q)
242{ 249{
243 /* We want to avoid unsigned->signed and truncation errors */ 250 /* We want to avoid unsigned->signed and truncation errors */
244 if (Q->vsz < P->vsz) return -1; 251 if (Q->memsize < P->memsize) return -1;
245 return Q->vsz != P->vsz; /* 0 if ==, 1 if > */ 252 return Q->memsize != P->memsize; /* 0 if ==, 1 if > */
246} 253}
247 254
248 255
@@ -283,9 +290,9 @@ static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
283#endif 290#endif
284 int ret; 291 int ret;
285 292
286 if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */) 293 if (!fgets(G.line_buf, LINE_BUF_SIZE, fp) || G.line_buf[0] != 'c' /* not "cpu" */)
287 return 0; 294 return 0;
288 ret = sscanf(line_buf, fmt, 295 ret = sscanf(G.line_buf, fmt,
289 &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle, 296 &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle,
290 &p_jif->iowait, &p_jif->irq, &p_jif->softirq, 297 &p_jif->iowait, &p_jif->irq, &p_jif->softirq,
291 &p_jif->steal); 298 &p_jif->steal);
@@ -362,7 +369,7 @@ static void do_stats(void)
362 369
363 get_jiffy_counts(); 370 get_jiffy_counts();
364 total_pcpu = 0; 371 total_pcpu = 0;
365 /* total_vsz = 0; */ 372 /* total_memsize = 0; */
366 new_hist = xmalloc(sizeof(new_hist[0]) * ntop); 373 new_hist = xmalloc(sizeof(new_hist[0]) * ntop);
367 /* 374 /*
368 * Make a pass through the data to get stats. 375 * Make a pass through the data to get stats.
@@ -394,7 +401,7 @@ static void do_stats(void)
394 i = (i+1) % prev_hist_count; 401 i = (i+1) % prev_hist_count;
395 /* hist_iterations++; */ 402 /* hist_iterations++; */
396 } while (i != last_i); 403 } while (i != last_i);
397 /* total_vsz += cur->vsz; */ 404 /* total_memsize += cur->memsize; */
398 } 405 }
399 406
400 /* 407 /*
@@ -407,6 +414,38 @@ static void do_stats(void)
407 414
408#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ 415#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
409 416
417static void print_line_buf(void)
418{
419 const char *fmt;
420
421 G.lines_remaining--;
422 fmt = OPT_BATCH_MODE ? "\n""%.*s" : "\n""%.*s"CLREOL;
423 if (!G.not_first_line) {
424 G.not_first_line = 1;
425 /* Go to top */
426 fmt = OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL;
427 }
428 printf(fmt, G.scr_width - 1, G.line_buf);
429}
430
431static void print_line_bold(void)
432{
433 G.lines_remaining--;
434//we never print first line in bold
435// if (!G.not_first_line) {
436// printf(OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL, G.scr_width - 1, G.line_buf);
437// G.not_first_line = 1;
438// } else {
439 printf(OPT_BATCH_MODE ? "\n""%.*s" : "\n"REVERSE"%.*s"NORMAL CLREOL, G.scr_width - 1, G.line_buf);
440// }
441}
442
443static void print_end(void)
444{
445 fputs_stdout(OPT_BATCH_MODE ? "\n" : CLREOS"\r");
446 G.not_first_line = 0; /* next print will be "first line" (will clear the screen) */
447}
448
410#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS 449#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
411/* formats 7 char string (8 with terminating NUL) */ 450/* formats 7 char string (8 with terminating NUL) */
412static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) 451static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
@@ -433,7 +472,7 @@ static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
433#endif 472#endif
434 473
435#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS 474#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
436static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) 475static void display_cpus(void)
437{ 476{
438 /* 477 /*
439 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100% 478 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100%
@@ -469,8 +508,8 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
469# else 508# else
470 /* Loop thru CPU(s) */ 509 /* Loop thru CPU(s) */
471 n_cpu_lines = smp_cpu_info ? num_cpus : 1; 510 n_cpu_lines = smp_cpu_info ? num_cpus : 1;
472 if (n_cpu_lines > *lines_rem_p) 511 if (n_cpu_lines > G.lines_remaining)
473 n_cpu_lines = *lines_rem_p; 512 n_cpu_lines = G.lines_remaining;
474 513
475 for (i = 0; i < n_cpu_lines; i++) { 514 for (i = 0; i < n_cpu_lines; i++) {
476 p_jif = &cpu_jif[i]; 515 p_jif = &cpu_jif[i];
@@ -488,7 +527,7 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
488 CALC_STAT(softirq); 527 CALC_STAT(softirq);
489 /*CALC_STAT(steal);*/ 528 /*CALC_STAT(steal);*/
490 529
491 snprintf(scrbuf, scr_width, 530 sprintf(G.line_buf,
492 /* Barely fits in 79 chars when in "decimals" mode. */ 531 /* Barely fits in 79 chars when in "decimals" mode. */
493# if ENABLE_FEATURE_TOP_SMP_CPU 532# if ENABLE_FEATURE_TOP_SMP_CPU
494 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", 533 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
@@ -501,16 +540,15 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
501 /*, SHOW_STAT(steal) - what is this 'steal' thing? */ 540 /*, SHOW_STAT(steal) - what is this 'steal' thing? */
502 /* I doubt anyone wants to know it */ 541 /* I doubt anyone wants to know it */
503 ); 542 );
504 puts(scrbuf); 543 print_line_buf();
505 } 544 }
506 } 545 }
507# undef SHOW_STAT 546# undef SHOW_STAT
508# undef CALC_STAT 547# undef CALC_STAT
509# undef FMT 548# undef FMT
510 *lines_rem_p -= i;
511} 549}
512#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */ 550#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */
513# define display_cpus(scr_width, scrbuf, lines_rem) ((void)0) 551# define display_cpus() ((void)0)
514#endif 552#endif
515 553
516enum { 554enum {
@@ -564,52 +602,55 @@ static void parse_meminfo(unsigned long meminfo[MI_MAX])
564 fclose(f); 602 fclose(f);
565} 603}
566 604
567static unsigned long display_header(int scr_width, int *lines_rem_p) 605static void cmdline_to_line_buf_and_print(unsigned offset, unsigned pid, const char *comm)
606{
607 int width = G.scr_width - offset;
608 if (width > 1) /* wider than to fit just the NUL? */
609 read_cmdline(G.line_buf + offset, width, pid, comm);
610//TODO: read_cmdline() sanitizes control chars, but not chars above 0x7e
611 print_line_buf();
612}
613
614static unsigned long display_header(void)
568{ 615{
569 char scrbuf[100]; /* [80] was a bit too low on 8Gb ram box */
570 char *buf; 616 char *buf;
571 unsigned long meminfo[MI_MAX]; 617 unsigned long meminfo[MI_MAX];
572 618
573 parse_meminfo(meminfo); 619 parse_meminfo(meminfo);
574 620
575 /* Output memory info */ 621 /* Output memory info */
576 if (scr_width > (int)sizeof(scrbuf)) 622 sprintf(G.line_buf,
577 scr_width = sizeof(scrbuf);
578 snprintf(scrbuf, scr_width,
579 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", 623 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
580 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE], 624 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE],
581 meminfo[MI_MEMFREE], 625 meminfo[MI_MEMFREE],
582 meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM], 626 meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM],
583 meminfo[MI_BUFFERS], 627 meminfo[MI_BUFFERS],
584 meminfo[MI_CACHED]); 628 meminfo[MI_CACHED]);
585 /* Go to top & clear to the end of screen */ 629 print_line_buf();
586 printf(OPT_BATCH_MODE ? "%s\n" : ESC"[H" ESC"[J" "%s\n", scrbuf);
587 (*lines_rem_p)--;
588 630
589 /* Display CPU time split as percentage of total time. 631 /* Display CPU time split as percentage of total time.
590 * This displays either a cumulative line or one line per CPU. 632 * This displays either a cumulative line or one line per CPU.
591 */ 633 */
592 display_cpus(scr_width, scrbuf, lines_rem_p); 634 display_cpus();
593 635
594 /* Read load average as a string */ 636 /* Read load average as a string */
595 buf = stpcpy(scrbuf, "Load average: "); 637 buf = stpcpy(G.line_buf, "Load average: ");
596 open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: ")); 638 open_read_close("loadavg", buf, sizeof(G.line_buf) - sizeof("Load average: "));
597 scrbuf[scr_width - 1] = '\0'; 639 G.line_buf[sizeof(G.line_buf) - 1] = '\0'; /* paranoia */
598 strchrnul(buf, '\n')[0] = '\0'; 640 strchrnul(buf, '\n')[0] = '\0';
599 puts(scrbuf); 641 print_line_buf();
600 (*lines_rem_p)--;
601 642
602 return meminfo[MI_MEMTOTAL]; 643 return meminfo[MI_MEMTOTAL];
603} 644}
604 645
605static NOINLINE void display_process_list(int lines_rem, int scr_width) 646static NOINLINE void display_process_list(void)
606{ 647{
607 enum { 648 enum {
608 BITS_PER_INT = sizeof(int) * 8 649 BITS_PER_INT = sizeof(int) * 8
609 }; 650 };
610 651
611 top_status_t *s; 652 top_status_t *s;
612 unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */ 653 unsigned long total_memory = display_header();
613 /* xxx_shift and xxx_scale variables allow us to replace 654 /* xxx_shift and xxx_scale variables allow us to replace
614 * expensive divides with multiply and shift */ 655 * expensive divides with multiply and shift */
615 unsigned pmem_shift, pmem_scale, pmem_half; 656 unsigned pmem_shift, pmem_scale, pmem_half;
@@ -621,7 +662,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
621 662
622#if ENABLE_FEATURE_TOP_DECIMALS 663#if ENABLE_FEATURE_TOP_DECIMALS
623# define UPSCALE 1000 664# define UPSCALE 1000
624typedef struct { unsigned quot, rem; } bb_div_t; 665 typedef struct { unsigned quot, rem; } bb_div_t;
625/* Used to have "div_t name = div((val), 10)" here 666/* Used to have "div_t name = div((val), 10)" here
626 * (IOW: intended to use libc-compatible way to divide and use 667 * (IOW: intended to use libc-compatible way to divide and use
627 * both result and remainder, but musl does not inline div()...) 668 * both result and remainder, but musl does not inline div()...)
@@ -629,28 +670,34 @@ typedef struct { unsigned quot, rem; } bb_div_t;
629 */ 670 */
630# define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 } 671# define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 }
631# define SHOW_STAT(name) name.quot, '0'+name.rem 672# define SHOW_STAT(name) name.quot, '0'+name.rem
673# define SANITIZE(name) if (name.quot > 99) name.quot = 99, name.rem = (unsigned char)('+' - '0')
632# define FMT "%3u.%c" 674# define FMT "%3u.%c"
633#else 675#else
634# define UPSCALE 100 676# define UPSCALE 100
635# define CALC_STAT(name, val) unsigned name = (val) 677# define CALC_STAT(name, val) unsigned name = (val)
678# define SANITIZE(name) if (name > 99) name = 99
636# define SHOW_STAT(name) name 679# define SHOW_STAT(name) name
637# define FMT "%4u%%" 680# define FMT "%4u%%"
638#endif 681#endif
639 682
640 /* what info of the processes is shown */ 683 strcpy(G.line_buf, " PID PPID USER STAT RSS %RSS"
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") 684 IF_FEATURE_TOP_SMP_PROCESS(" CPU")
644 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU") 685 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
645 " COMMAND"); 686 " COMMAND");
646 lines_rem--; 687 print_line_bold();
647 688
648 /* 689 /* %RSS = s->memsize / MemTotal * 100%
649 * %VSZ = s->vsz/MemTotal 690 * Calculate this with multiply and shift. Example:
691 * shift = 12
692 * scale = 100 * 0x1000 / total_memory
693 * percent_mem = (size_mem * scale) >> shift
694 * ~= (size_mem >> shift) * scale
695 * ~= (size_mem >> shift) * 100 * (1 << shift) / total_memory
696 * ~= size_mem * 100 / total_memory
650 */ 697 */
651 pmem_shift = BITS_PER_INT-11; 698 pmem_shift = BITS_PER_INT-11;
652 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory; 699 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
653 /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */ 700 /* s->memsize is in kb. we want (s->memsize * pmem_scale) to never overflow */
654 while (pmem_scale >= 512) { 701 while (pmem_scale >= 512) {
655 pmem_scale /= 4; 702 pmem_scale /= 4;
656 pmem_shift -= 2; 703 pmem_shift -= 2;
@@ -689,25 +736,29 @@ typedef struct { unsigned quot, rem; } bb_div_t;
689 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2); 736 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
690 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ 737 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
691#endif 738#endif
739 if (G.lines_remaining > ntop - G_scroll_ofs)
740 G.lines_remaining = ntop - G_scroll_ofs;
692 741
693 /* Ok, all preliminary data is ready, go through the list */ 742 /* Ok, all preliminary data is ready, go through the list */
694 scr_width += 2; /* account for leading '\n' and trailing NUL */
695 if (lines_rem > ntop - G_scroll_ofs)
696 lines_rem = ntop - G_scroll_ofs;
697 s = top + G_scroll_ofs; 743 s = top + G_scroll_ofs;
698 while (--lines_rem >= 0) { 744 while (G.lines_remaining > 0) {
699 int n; 745 int n;
700 char *ppu; 746 char *ppu;
701 char ppubuf[sizeof(int)*3 * 2 + 12]; 747 char ppubuf[sizeof(int)*3 * 2 + 12];
702 char vsz_str_buf[8]; 748 char memsize_str_buf[8];
703 unsigned col; 749 unsigned col;
704 750
705 CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift); 751 CALC_STAT(pmem, (s->memsize*pmem_scale + pmem_half) >> pmem_shift);
706#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 752#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
707 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift); 753 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
708#endif 754#endif
755 /* VSZ can be much larger than total memory
756 * (seen values close to 2Tbyte), don't try to display
757 * "uses 12345.6% of MemTotal" (won't fit the column)
758 */
759 SANITIZE(pmem);
709 760
710 smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy"); 761 smart_ulltoa5(s->memsize, memsize_str_buf, " mgtpezy");
711 /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */ 762 /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */
712 n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid)); 763 n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid));
713 ppu = ppubuf; 764 ppu = ppubuf;
@@ -736,27 +787,23 @@ typedef struct { unsigned quot, rem; } bb_div_t;
736 ppu[6+6+8] = '\0'; /* truncate USER */ 787 ppu[6+6+8] = '\0'; /* truncate USER */
737 } 788 }
738 shortened: 789 shortened:
739 col = snprintf(line_buf, scr_width, 790 col = sprintf(G.line_buf,
740 "\n" "%s %s %.5s" FMT 791 "%s %s %.5s" FMT
741 IF_FEATURE_TOP_SMP_PROCESS(" %3d") 792 IF_FEATURE_TOP_SMP_PROCESS(" %3d")
742 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) 793 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
743 " ", 794 " ",
744 ppu, 795 ppu,
745 s->state, vsz_str_buf, 796 s->state, memsize_str_buf,
746 SHOW_STAT(pmem) 797 SHOW_STAT(pmem)
747 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) 798 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
748 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu)) 799 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
749 ); 800 );
750 if ((int)(scr_width - col) > 1) 801 cmdline_to_line_buf_and_print(col, s->pid, s->comm);
751 read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm);
752 fputs_stdout(line_buf);
753 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu, 802 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
754 cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */ 803 cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */
755 s++; 804 s++;
756 } 805 }
757 /* printf(" %d", hist_iterations); */ 806 /* printf(" %d", hist_iterations); */
758 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
759 fflush_all();
760} 807}
761#undef UPSCALE 808#undef UPSCALE
762#undef SHOW_STAT 809#undef SHOW_STAT
@@ -828,36 +875,34 @@ static int topmem_sort(char *a, char *b)
828} 875}
829 876
830/* display header info (meminfo / loadavg) */ 877/* display header info (meminfo / loadavg) */
831static void display_topmem_header(int scr_width, int *lines_rem_p) 878static void display_topmem_header(void)
832{ 879{
833 unsigned long meminfo[MI_MAX]; 880 unsigned long meminfo[MI_MAX];
834 881
835 parse_meminfo(meminfo); 882 parse_meminfo(meminfo);
836 883
837 snprintf(line_buf, LINE_BUF_SIZE, 884 sprintf(G.line_buf,
838 "Mem total:%lu anon:%lu map:%lu free:%lu", 885 "Mem total:%lu anon:%lu map:%lu free:%lu",
839 meminfo[MI_MEMTOTAL], 886 meminfo[MI_MEMTOTAL],
840 meminfo[MI_ANONPAGES], 887 meminfo[MI_ANONPAGES],
841 meminfo[MI_MAPPED], 888 meminfo[MI_MAPPED],
842 meminfo[MI_MEMFREE]); 889 meminfo[MI_MEMFREE]);
843 printf(OPT_BATCH_MODE ? "%.*s\n" : ESC"[H" ESC"[J" "%.*s\n", scr_width, line_buf); 890 print_line_buf();
844 891
845 snprintf(line_buf, LINE_BUF_SIZE, 892 sprintf(G.line_buf,
846 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", 893 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu",
847 meminfo[MI_SLAB], 894 meminfo[MI_SLAB],
848 meminfo[MI_BUFFERS], 895 meminfo[MI_BUFFERS],
849 meminfo[MI_CACHED], 896 meminfo[MI_CACHED],
850 meminfo[MI_DIRTY], 897 meminfo[MI_DIRTY],
851 meminfo[MI_WRITEBACK]); 898 meminfo[MI_WRITEBACK]);
852 printf("%.*s\n", scr_width, line_buf); 899 print_line_buf();
853 900
854 snprintf(line_buf, LINE_BUF_SIZE, 901 sprintf(G.line_buf,
855 "Swap total:%lu free:%lu", // TODO: % used? 902 "Swap total:%lu free:%lu", // TODO: % used?
856 meminfo[MI_SWAPTOTAL], 903 meminfo[MI_SWAPTOTAL],
857 meminfo[MI_SWAPFREE]); 904 meminfo[MI_SWAPFREE]);
858 printf("%.*s\n", scr_width, line_buf); 905 print_line_buf();
859
860 (*lines_rem_p) -= 3;
861} 906}
862 907
863/* see http://en.wikipedia.org/wiki/Tera */ 908/* see http://en.wikipedia.org/wiki/Tera */
@@ -870,75 +915,57 @@ static void ulltoa4_and_space(unsigned long long ul, char buf[5])
870 smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' '; 915 smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' ';
871} 916}
872 917
873static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width) 918static NOINLINE void display_topmem_process_list(void)
874{ 919{
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; 920 const topmem_status_t *s = topmem + G_scroll_ofs;
878 char *cp, ch; 921 char *cp, ch;
879 922
880 display_topmem_header(scr_width, &lines_rem); 923 display_topmem_header();
881 924
882 strcpy(line_buf, HDR_STR " COMMAND"); 925 strcpy(G.line_buf, " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK COMMAND");
883 /* Mark the ^FIELD^ we sort by */ 926 /* Mark the ^FIELD^ we sort by */
884 cp = &line_buf[5 + sort_field * 6]; 927 cp = &G.line_buf[5 + sort_field * 6];
885 ch = "^_"[inverted]; 928 ch = "^_"[inverted];
886 cp[6] = ch; 929 cp[6] = ch;
887 do *cp++ = ch; while (*cp == ' '); 930 do *cp++ = ch; while (*cp == ' ');
931 print_line_bold();
888 932
889 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width, line_buf); 933 if (G.lines_remaining > ntop - G_scroll_ofs)
890 lines_rem--; 934 G.lines_remaining = ntop - G_scroll_ofs;
891 935 while (G.lines_remaining > 0) {
892 if (lines_rem > ntop - G_scroll_ofs)
893 lines_rem = ntop - G_scroll_ofs;
894 while (--lines_rem >= 0) {
895 /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */ 936 /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
896 int n = sprintf(line_buf, "%5u ", s->pid); 937 int n = sprintf(G.line_buf, "%5u ", s->pid);
897 if (n > 7) { 938 if (n > 7) {
898 /* PID is 7 chars long (up to 4194304) */ 939 /* PID is 7 chars long (up to 4194304) */
899 ulltoa4_and_space(s->vsz , &line_buf[8]); 940 ulltoa4_and_space(s->vsz , &G.line_buf[8]);
900 ulltoa4_and_space(s->vszrw, &line_buf[8+5]); 941 ulltoa4_and_space(s->vszrw, &G.line_buf[8+5]);
901 /* the next field (RSS) starts at 8+10 = 3*6 */ 942 /* the next field (RSS) starts at 8+10 = 3*6 */
902 } else { 943 } else {
903 if (n == 7) /* PID is 6 chars long */ 944 if (n == 7) /* PID is 6 chars long */
904 ulltoa4_and_space(s->vsz, &line_buf[7]); 945 ulltoa4_and_space(s->vsz, &G.line_buf[7]);
905 /* the next field (VSZRW) starts at 7+5 = 2*6 */ 946 /* the next field (VSZRW) starts at 7+5 = 2*6 */
906 else /* PID is 5 chars or less */ 947 else /* PID is 5 chars or less */
907 ulltoa5_and_space(s->vsz, &line_buf[6]); 948 ulltoa5_and_space(s->vsz, &G.line_buf[6]);
908 ulltoa5_and_space(s->vszrw, &line_buf[2*6]); 949 ulltoa5_and_space(s->vszrw, &G.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 } 950 }
919 printf("\n""%.*s", scr_width, line_buf); 951 ulltoa5_and_space(s->rss , &G.line_buf[3*6]);
952 ulltoa5_and_space(s->rss_sh , &G.line_buf[4*6]);
953 ulltoa5_and_space(s->dirty , &G.line_buf[5*6]);
954 ulltoa5_and_space(s->dirty_sh, &G.line_buf[6*6]);
955 ulltoa5_and_space(s->stack , &G.line_buf[7*6]);
956 G.line_buf[8*6] = '\0';
957 cmdline_to_line_buf_and_print(8*6, s->pid, s->comm);
920 s++; 958 s++;
921 } 959 }
922 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
923 fflush_all();
924#undef HDR_STR
925#undef MIN_WIDTH
926} 960}
927 961
928#else 962#endif /* end TOPMEM support */
929void display_topmem_process_list(int lines_rem, int scr_width);
930int topmem_sort(char *a, char *b);
931#endif /* TOPMEM */
932
933/*
934 * end TOPMEM support
935 */
936 963
937enum { 964enum {
938 TOP_MASK = 0 965 TOP_MASK = 0
939 | PSSCAN_PID 966 | PSSCAN_PID
940 | PSSCAN_PPID 967 | PSSCAN_PPID
941 | PSSCAN_VSZ 968 | PSSCAN_RSS
942 | PSSCAN_STIME 969 | PSSCAN_STIME
943 | PSSCAN_UTIME 970 | PSSCAN_UTIME
944 | PSSCAN_STATE 971 | PSSCAN_STATE
@@ -950,7 +977,7 @@ enum {
950 | PSSCAN_SMAPS 977 | PSSCAN_SMAPS
951 | PSSCAN_COMM, 978 | PSSCAN_COMM,
952 EXIT_MASK = 0, 979 EXIT_MASK = 0,
953 NO_RESCAN_MASK = (unsigned)-1, 980 ONLY_REDRAW = (unsigned)-1,
954}; 981};
955 982
956#if ENABLE_FEATURE_TOP_INTERACTIVE 983#if ENABLE_FEATURE_TOP_INTERACTIVE
@@ -963,15 +990,22 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
963 } 990 }
964 991
965 while (1) { 992 while (1) {
966 int32_t c; 993 int32_t c, cc;
967 994
968 c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000); 995 c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
969 if (c == -1 && errno != EAGAIN) { 996 if (c == -1) {
970 /* error/EOF */ 997 if (errno != EAGAIN)
971 option_mask32 |= OPT_EOF; 998 /* error/EOF */
999 option_mask32 |= OPT_EOF;
1000 /* else: timeout - rescan and refresh */
972 break; 1001 break;
973 } 1002 }
974 interval = 0; 1003 interval = 0;
1004 /* "continue" statements below return to do one additional
1005 * quick attempt to read a key. This prevents
1006 * long sequence of e.g. "nnnnnnnnnnnnnnnnnnnnnnnnnn"
1007 * to cause lots of rescans.
1008 */
975 1009
976 if (c == initial_settings.c_cc[VINTR]) 1010 if (c == initial_settings.c_cc[VINTR])
977 return EXIT_MASK; 1011 return EXIT_MASK;
@@ -1005,9 +1039,10 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1005 G_scroll_ofs = ntop - 1; 1039 G_scroll_ofs = ntop - 1;
1006 if (G_scroll_ofs < 0) 1040 if (G_scroll_ofs < 0)
1007 G_scroll_ofs = 0; 1041 G_scroll_ofs = 0;
1008 return NO_RESCAN_MASK; 1042 return ONLY_REDRAW;
1009 } 1043 }
1010 1044
1045 cc = c;
1011 c |= 0x20; /* lowercase */ 1046 c |= 0x20; /* lowercase */
1012 if (c == 'q') 1047 if (c == 'q')
1013 return EXIT_MASK; 1048 return EXIT_MASK;
@@ -1055,9 +1090,17 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1055 continue; 1090 continue;
1056 } 1091 }
1057# if ENABLE_FEATURE_TOPMEM 1092# if ENABLE_FEATURE_TOPMEM
1093 if (cc == 'S') {
1094 if (--sort_field < 0)
1095 sort_field = NUM_SORT_FIELD - 1;
1096 if (--sort_field < 0)
1097 sort_field = NUM_SORT_FIELD - 1;
1098 }
1058 if (c == 's') { 1099 if (c == 's') {
1059 scan_mask = TOPMEM_MASK;
1060 sort_field = (sort_field + 1) % NUM_SORT_FIELD; 1100 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1101 if (scan_mask == TOPMEM_MASK)
1102 return ONLY_REDRAW;
1103 scan_mask = TOPMEM_MASK;
1061 free(prev_hist); 1104 free(prev_hist);
1062 prev_hist = NULL; 1105 prev_hist = NULL;
1063 prev_hist_count = 0; 1106 prev_hist_count = 0;
@@ -1066,7 +1109,7 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1066# endif 1109# endif
1067 if (c == 'r') { 1110 if (c == 'r') {
1068 inverted ^= 1; 1111 inverted ^= 1;
1069 continue; 1112 return ONLY_REDRAW;
1070 } 1113 }
1071# if ENABLE_FEATURE_TOP_SMP_CPU 1114# if ENABLE_FEATURE_TOP_SMP_CPU
1072 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ 1115 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
@@ -1088,8 +1131,8 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1088 } 1131 }
1089# endif 1132# endif
1090# endif 1133# endif
1091 break; /* unknown key -> force refresh */ 1134 /* Unknown key. Eat remaining buffered input (if any) */
1092 } 1135 } /* while (1) */
1093 1136
1094 return scan_mask; 1137 return scan_mask;
1095} 1138}
@@ -1155,7 +1198,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1155{ 1198{
1156 duration_t interval; 1199 duration_t interval;
1157 int iterations; 1200 int iterations;
1158 unsigned col; 1201 unsigned opt;
1159 char *str_interval, *str_iterations; 1202 char *str_interval, *str_iterations;
1160 unsigned scan_mask = TOP_MASK; 1203 unsigned scan_mask = TOP_MASK;
1161 1204
@@ -1172,13 +1215,13 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1172 1215
1173 /* all args are options; -n NUM */ 1216 /* all args are options; -n NUM */
1174 make_all_argv_opts(argv); /* options can be specified w/o dash */ 1217 make_all_argv_opts(argv); /* options can be specified w/o dash */
1175 col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations); 1218 opt = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations);
1176 /* NB: -m and -H are accepted even if not configured */ 1219 /* NB: -m and -H are accepted even if not configured */
1177#if ENABLE_FEATURE_TOPMEM 1220#if ENABLE_FEATURE_TOPMEM
1178 if (col & OPT_m) /* -m (busybox specific) */ 1221 if (opt & OPT_m) /* -m (busybox specific) */
1179 scan_mask = TOPMEM_MASK; 1222 scan_mask = TOPMEM_MASK;
1180#endif 1223#endif
1181 if (col & OPT_d) { 1224 if (opt & OPT_d) {
1182 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */ 1225 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */
1183 if (str_interval[0] == '-') 1226 if (str_interval[0] == '-')
1184 str_interval++; 1227 str_interval++;
@@ -1187,18 +1230,17 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1187 if (interval > INT_MAX / 1000) 1230 if (interval > INT_MAX / 1000)
1188 interval = INT_MAX / 1000; 1231 interval = INT_MAX / 1000;
1189 } 1232 }
1190 if (col & OPT_n) { 1233 if (opt & OPT_n) {
1191 if (str_iterations[0] == '-') 1234 if (str_iterations[0] == '-')
1192 str_iterations++; 1235 str_iterations++;
1193 iterations = xatou(str_iterations); 1236 iterations = xatou(str_iterations);
1194 } 1237 }
1195#if ENABLE_FEATURE_SHOW_THREADS 1238#if ENABLE_FEATURE_SHOW_THREADS
1196 if (col & OPT_H) { 1239 if (opt & OPT_H) {
1197 scan_mask |= PSSCAN_TASKS; 1240 scan_mask |= PSSCAN_TASKS;
1198 } 1241 }
1199#endif 1242#endif
1200 1243
1201 /* change to /proc */
1202 xchdir("/proc"); 1244 xchdir("/proc");
1203 1245
1204#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1246#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
@@ -1226,23 +1268,22 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1226#endif 1268#endif
1227 1269
1228 while (scan_mask != EXIT_MASK) { 1270 while (scan_mask != EXIT_MASK) {
1229 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask;) 1271 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask = scan_mask;)
1230 procps_status_t *p = NULL; 1272 procps_status_t *p = NULL;
1231 1273
1232 if (OPT_BATCH_MODE) { 1274 G.lines = INT_MAX;
1233 G.lines = INT_MAX; 1275 G.scr_width = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
1234 col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */ 1276 if (!OPT_BATCH_MODE) {
1235 } else {
1236 G.lines = 24; /* default */ 1277 G.lines = 24; /* default */
1237 col = 79; 1278 G.scr_width = 80;
1238 /* We output to stdout, we need size of stdout (not stdin)! */ 1279 /* We output to stdout, we need size of stdout (not stdin)! */
1239 get_terminal_width_height(STDOUT_FILENO, &col, &G.lines); 1280 get_terminal_width_height(STDOUT_FILENO, &G.scr_width, &G.lines);
1240 if (G.lines < 5 || col < 10) { 1281 if (G.lines < 5 || G.scr_width < 10) {
1241 sleep_for_duration(interval); 1282 sleep_for_duration(interval);
1242 continue; 1283 continue;
1243 } 1284 }
1244 if (col > LINE_BUF_SIZE - 2) 1285 if (G.scr_width > LINE_BUF_SIZE - 2)
1245 col = LINE_BUF_SIZE - 2; 1286 G.scr_width = LINE_BUF_SIZE - 2;
1246 } 1287 }
1247 1288
1248 /* read process IDs & status for all the processes */ 1289 /* read process IDs & status for all the processes */
@@ -1255,7 +1296,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1255 top = xrealloc_vector(top, 6, ntop++); 1296 top = xrealloc_vector(top, 6, ntop++);
1256 top[n].pid = p->pid; 1297 top[n].pid = p->pid;
1257 top[n].ppid = p->ppid; 1298 top[n].ppid = p->ppid;
1258 top[n].vsz = p->vsz; 1299 top[n].memsize = p->rss;
1259#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1300#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1260 top[n].ticks = p->stime + p->utime; 1301 top[n].ticks = p->stime + p->utime;
1261#endif 1302#endif
@@ -1268,20 +1309,20 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1268 } 1309 }
1269#if ENABLE_FEATURE_TOPMEM 1310#if ENABLE_FEATURE_TOPMEM
1270 else { /* TOPMEM */ 1311 else { /* TOPMEM */
1271 if (!(p->smaps.mapped_ro | p->smaps.mapped_rw)) 1312 if (!(p->mapped_ro | p->mapped_rw))
1272 continue; /* kernel threads are ignored */ 1313 continue; /* kernel threads are ignored */
1273 n = ntop; 1314 n = ntop;
1274 /* No bug here - top and topmem are the same */ 1315 /* No bug here - top and topmem are the same */
1275 top = xrealloc_vector(topmem, 6, ntop++); 1316 top = xrealloc_vector(topmem, 6, ntop++);
1276 strcpy(topmem[n].comm, p->comm); 1317 strcpy(topmem[n].comm, p->comm);
1277 topmem[n].pid = p->pid; 1318 topmem[n].pid = p->pid;
1278 topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro; 1319 topmem[n].vsz = p->mapped_rw + p->mapped_ro;
1279 topmem[n].vszrw = p->smaps.mapped_rw; 1320 topmem[n].vszrw = p->mapped_rw;
1280 topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty; 1321 topmem[n].rss_sh = p->shared_clean + p->shared_dirty;
1281 topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh; 1322 topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh;
1282 topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty; 1323 topmem[n].dirty = p->private_dirty + p->shared_dirty;
1283 topmem[n].dirty_sh = p->smaps.shared_dirty; 1324 topmem[n].dirty_sh = p->shared_dirty;
1284 topmem[n].stack = p->smaps.stack; 1325 topmem[n].stack = p->stack;
1285 } 1326 }
1286#endif 1327#endif
1287 } /* end of "while we read /proc" */ 1328 } /* end of "while we read /proc" */
@@ -1310,30 +1351,40 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1310 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); 1351 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1311 } 1352 }
1312#endif 1353#endif
1313 IF_FEATURE_TOP_INTERACTIVE(display:) 1354 IF_FEATURE_TOP_INTERACTIVE(redraw:)
1355 G.lines_remaining = G.lines;
1314 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) { 1356 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1315 display_process_list(G.lines, col); 1357 display_process_list();
1316 } 1358 }
1317#if ENABLE_FEATURE_TOPMEM 1359#if ENABLE_FEATURE_TOPMEM
1318 else { /* TOPMEM */ 1360 else { /* TOPMEM */
1319 display_topmem_process_list(G.lines, col); 1361 display_topmem_process_list();
1320 } 1362 }
1321#endif 1363#endif
1364 print_end();
1365 fflush_all();
1322 if (iterations >= 0 && !--iterations) 1366 if (iterations >= 0 && !--iterations)
1323 break; 1367 break;
1324#if !ENABLE_FEATURE_TOP_INTERACTIVE 1368#if !ENABLE_FEATURE_TOP_INTERACTIVE
1325 clearmems(); 1369 clearmems();
1326 sleep_for_duration(interval); 1370 sleep_for_duration(interval);
1327#else 1371#else
1328 new_mask = handle_input(scan_mask, interval); 1372 new_mask = handle_input(scan_mask,
1329 if (new_mask == NO_RESCAN_MASK) 1373 /* After "redraw with no rescan", have one
1330 goto display; 1374 * key timeout shorter that normal
1375 * (IOW: rescan sooner):
1376 */
1377 (new_mask == ONLY_REDRAW ? 1 : interval)
1378 );
1379 if (new_mask == ONLY_REDRAW)
1380 goto redraw;
1331 scan_mask = new_mask; 1381 scan_mask = new_mask;
1332 clearmems(); 1382 clearmems();
1333#endif 1383#endif
1334 } /* end of "while (not Q)" */ 1384 } /* end of "while (not Q)" */
1335 1385
1336 bb_putchar('\n'); 1386 if (!OPT_BATCH_MODE)
1387 bb_putchar('\n');
1337#if ENABLE_FEATURE_TOP_INTERACTIVE 1388#if ENABLE_FEATURE_TOP_INTERACTIVE
1338 reset_term(); 1389 reset_term();
1339#endif 1390#endif
diff --git a/procps/watch.c b/procps/watch.c
index 05b72723c..d7c3ad55e 100644
--- a/procps/watch.c
+++ b/procps/watch.c
@@ -71,9 +71,16 @@ int watch_main(int argc UNUSED_PARAM, char **argv)
71 71
72 // watch from both procps 2.x and 3.x does concatenation. Example: 72 // watch from both procps 2.x and 3.x does concatenation. Example:
73 // watch ls -l "a /tmp" "2>&1" - ls won't see "a /tmp" as one param 73 // watch ls -l "a /tmp" "2>&1" - ls won't see "a /tmp" as one param
74#if ENABLE_PLATFORM_MINGW32
75 cmd = NULL;
76 do {
77 cmd = xappendword(cmd, *argv);
78 } while (*++argv);
79#else
74 cmd = *argv; 80 cmd = *argv;
75 while (*++argv) 81 while (*++argv)
76 cmd = xasprintf("%s %s", cmd, *argv); // leaks cmd 82 cmd = xasprintf("%s %s", cmd, *argv); // leaks cmd
83#endif
77 84
78 period = parse_duration_str(period_str); 85 period = parse_duration_str(period_str);
79 width = (unsigned)-1; // make sure first time new_width != width 86 width = (unsigned)-1; // make sure first time new_width != width