diff options
Diffstat (limited to 'procps')
-rw-r--r-- | procps/free.c | 9 | ||||
-rw-r--r-- | procps/iostat.c | 2 | ||||
-rw-r--r-- | procps/kill.c | 2 | ||||
-rw-r--r-- | procps/mpstat.c | 2 | ||||
-rw-r--r-- | procps/pgrep.c | 55 | ||||
-rw-r--r-- | procps/pmap.c | 126 | ||||
-rw-r--r-- | procps/ps.c | 16 | ||||
-rw-r--r-- | procps/smemcap.c | 1 | ||||
-rw-r--r-- | procps/top.c | 351 | ||||
-rw-r--r-- | procps/watch.c | 7 |
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) */ |
81 | static NOINLINE unsigned int parse_meminfo(struct globals *g) | 82 | static 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 | ||
108 | static 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 | ||
107 | int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 116 | int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
108 | int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM)) | 117 | int 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 |
29 | typedef unsigned long long cputime_t; | 29 | typedef unsigned long long cputime_t; |
30 | typedef long long icputime_t; | 30 | typedef 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 |
34 | typedef unsigned long cputime_t; | 34 | typedef 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 |
50 | typedef unsigned long long data_t; | 50 | typedef unsigned long long data_t; |
51 | typedef long long idata_t; | 51 | typedef 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 |
55 | typedef unsigned long data_t; | 55 | typedef 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 | ||
92 | static void act(unsigned pid, char *cmd, int signo) | 132 | static 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 | ||
45 | static void print_smaprec(struct smaprec *currec, void *data) | 49 | struct 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 | |||
69 | static 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 | */ | ||
90 | static 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 | ||
63 | static int procps_get_maps(pid_t pid, unsigned opt) | 161 | static 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 |
121 | static unsigned long get_uptime(void) | 121 | static 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 | ||
240 | static void func_state(char *buf, int size, const procps_status_t *ps) | 241 | static 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 | ||
245 | static void func_args(char *buf, int size, const procps_status_t *ps) | 247 | static 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 | ||
260 | static void func_pgid(char *buf, int size, const procps_status_t *ps) | 263 | static 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 |
298 | static void func_rgroup(char *buf, int size, const procps_status_t *ps) | 302 | static 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 | ||
24 | struct fileblock { | 25 | struct 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 | ||
122 | typedef struct top_status_t { | 127 | typedef 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) | |||
241 | static int mem_sort(top_status_t *P, top_status_t *Q) | 248 | static 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 | ||
417 | static 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 | |||
431 | static 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 | |||
443 | static 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) */ |
412 | static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) | 451 | static 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 |
436 | static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) | 475 | static 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 | ||
516 | enum { | 554 | enum { |
@@ -564,52 +602,55 @@ static void parse_meminfo(unsigned long meminfo[MI_MAX]) | |||
564 | fclose(f); | 602 | fclose(f); |
565 | } | 603 | } |
566 | 604 | ||
567 | static unsigned long display_header(int scr_width, int *lines_rem_p) | 605 | static 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 | |||
614 | static 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 | ||
605 | static NOINLINE void display_process_list(int lines_rem, int scr_width) | 646 | static 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 |
624 | typedef 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) */ |
831 | static void display_topmem_header(int scr_width, int *lines_rem_p) | 878 | static 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 | ||
873 | static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width) | 918 | static 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 */ |
929 | void display_topmem_process_list(int lines_rem, int scr_width); | ||
930 | int topmem_sort(char *a, char *b); | ||
931 | #endif /* TOPMEM */ | ||
932 | |||
933 | /* | ||
934 | * end TOPMEM support | ||
935 | */ | ||
936 | 963 | ||
937 | enum { | 964 | enum { |
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 |