diff options
Diffstat (limited to '')
-rw-r--r-- | libbb/procps.c | 177 |
1 files changed, 86 insertions, 91 deletions
diff --git a/libbb/procps.c b/libbb/procps.c index f56b71b21..c751100bc 100644 --- a/libbb/procps.c +++ b/libbb/procps.c | |||
@@ -63,6 +63,7 @@ const char* FAST_FUNC get_cached_groupname(gid_t gid) | |||
63 | return get_cached(1, gid, gid2group_utoa); | 63 | return get_cached(1, gid, gid2group_utoa); |
64 | } | 64 | } |
65 | 65 | ||
66 | #if !ENABLE_PLATFORM_MINGW32 | ||
66 | 67 | ||
67 | #define PROCPS_BUFSIZE 1024 | 68 | #define PROCPS_BUFSIZE 1024 |
68 | 69 | ||
@@ -109,7 +110,7 @@ void FAST_FUNC free_procps_scan(procps_status_t* sp) | |||
109 | } | 110 | } |
110 | 111 | ||
111 | #if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP | 112 | #if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP |
112 | static unsigned long long fast_strtoull_16(char **endptr) | 113 | unsigned long long FAST_FUNC fast_strtoull_16(char **endptr) |
113 | { | 114 | { |
114 | unsigned char c; | 115 | unsigned char c; |
115 | char *str = *endptr; | 116 | char *str = *endptr; |
@@ -130,7 +131,7 @@ static unsigned long long fast_strtoull_16(char **endptr) | |||
130 | 131 | ||
131 | #if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP | 132 | #if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP |
132 | /* We cut a lot of corners here for speed */ | 133 | /* We cut a lot of corners here for speed */ |
133 | static unsigned long fast_strtoul_10(char **endptr) | 134 | unsigned long FAST_FUNC fast_strtoul_10(char **endptr) |
134 | { | 135 | { |
135 | unsigned char c; | 136 | unsigned char c; |
136 | char *str = *endptr; | 137 | char *str = *endptr; |
@@ -143,6 +144,24 @@ static unsigned long fast_strtoul_10(char **endptr) | |||
143 | *endptr = str + 1; /* We skip trailing space! */ | 144 | *endptr = str + 1; /* We skip trailing space! */ |
144 | return n; | 145 | return n; |
145 | } | 146 | } |
147 | # if LONG_MAX < LLONG_MAX | ||
148 | /* For VSZ, which can be very large */ | ||
149 | static unsigned long long fast_strtoull_10(char **endptr) | ||
150 | { | ||
151 | unsigned char c; | ||
152 | char *str = *endptr; | ||
153 | unsigned long long n = *str - '0'; | ||
154 | |||
155 | /* Need to stop on both ' ' and '\n' */ | ||
156 | while ((c = *++str) > ' ') | ||
157 | n = n*10 + (c - '0'); | ||
158 | |||
159 | *endptr = str + 1; /* We skip trailing space! */ | ||
160 | return n; | ||
161 | } | ||
162 | # else | ||
163 | # define fast_strtoull_10(endptr) fast_strtoul_10(endptr) | ||
164 | # endif | ||
146 | 165 | ||
147 | # if ENABLE_FEATURE_FAST_TOP | 166 | # if ENABLE_FEATURE_FAST_TOP |
148 | static long fast_strtol_10(char **endptr) | 167 | static long fast_strtol_10(char **endptr) |
@@ -155,7 +174,7 @@ static long fast_strtol_10(char **endptr) | |||
155 | } | 174 | } |
156 | # endif | 175 | # endif |
157 | 176 | ||
158 | static char *skip_fields(char *str, int count) | 177 | char* FAST_FUNC skip_fields(char *str, int count) |
159 | { | 178 | { |
160 | do { | 179 | do { |
161 | while (*str++ != ' ') | 180 | while (*str++ != ' ') |
@@ -166,35 +185,25 @@ static char *skip_fields(char *str, int count) | |||
166 | } | 185 | } |
167 | #endif | 186 | #endif |
168 | 187 | ||
169 | #if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP | 188 | #if ENABLE_FEATURE_TOPMEM |
170 | static char* skip_whitespace_if_prefixed_with(char *buf, const char *prefix) | 189 | static NOINLINE void procps_read_smaps(pid_t pid, procps_status_t *sp) |
171 | { | 190 | { |
172 | char *tp = is_prefixed_with(buf, prefix); | 191 | // There is A LOT of /proc/PID/smaps data on a big system. |
173 | if (tp) { | 192 | // Optimize this for speed, makes "top -m" faster. |
174 | tp = skip_whitespace(tp); | 193 | //TODO large speedup: |
175 | } | 194 | //read /proc/PID/smaps_rollup (cumulative stats of all mappings, much faster) |
176 | return tp; | 195 | //and /proc/PID/maps to get mapped_ro and mapped_rw (IOW: VSZ,VSZRW) |
177 | } | ||
178 | 196 | ||
179 | int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, | ||
180 | void (*cb)(struct smaprec *, void *), void *data) | ||
181 | { | ||
182 | FILE *file; | 197 | FILE *file; |
183 | struct smaprec currec; | ||
184 | char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; | 198 | char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; |
185 | char buf[PROCPS_BUFSIZE]; | 199 | char buf[PROCPS_BUFSIZE]; |
186 | #if !ENABLE_PMAP | ||
187 | void (*cb)(struct smaprec *, void *) = NULL; | ||
188 | void *data = NULL; | ||
189 | #endif | ||
190 | 200 | ||
191 | sprintf(filename, "/proc/%u/smaps", (int)pid); | 201 | sprintf(filename, "/proc/%u/smaps", (int)pid); |
192 | 202 | ||
193 | file = fopen_for_read(filename); | 203 | file = fopen_for_read(filename); |
194 | if (!file) | 204 | if (!file) |
195 | return 1; | 205 | return; |
196 | 206 | ||
197 | memset(&currec, 0, sizeof(currec)); | ||
198 | while (fgets(buf, PROCPS_BUFSIZE, file)) { | 207 | while (fgets(buf, PROCPS_BUFSIZE, file)) { |
199 | // Each mapping datum has this form: | 208 | // Each mapping datum has this form: |
200 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME | 209 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME |
@@ -202,80 +211,53 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, | |||
202 | // Rss: nnn kB | 211 | // Rss: nnn kB |
203 | // ..... | 212 | // ..... |
204 | 213 | ||
205 | char *tp, *p; | 214 | char *tp; |
206 | 215 | ||
216 | if (buf[0] == 'S' || buf[0] == 'P') { | ||
207 | #define SCAN(S, X) \ | 217 | #define SCAN(S, X) \ |
208 | if ((tp = skip_whitespace_if_prefixed_with(buf, S)) != NULL) { \ | 218 | if (memcmp(buf, S, sizeof(S)-1) == 0) { \ |
209 | total->X += currec.X = fast_strtoul_10(&tp); \ | 219 | tp = skip_whitespace(buf + sizeof(S)-1); \ |
210 | continue; \ | 220 | sp->X += fast_strtoul_10(&tp); \ |
211 | } | 221 | continue; \ |
212 | if (cb) { | 222 | } |
213 | SCAN("Pss:" , smap_pss ); | 223 | SCAN("Private_Dirty:", private_dirty) |
214 | SCAN("Swap:" , smap_swap ); | 224 | SCAN("Private_Clean:", private_clean) |
215 | } | 225 | SCAN("Shared_Dirty:" , shared_dirty ) |
216 | SCAN("Private_Dirty:", private_dirty); | 226 | SCAN("Shared_Clean:" , shared_clean ) |
217 | SCAN("Private_Clean:", private_clean); | ||
218 | SCAN("Shared_Dirty:" , shared_dirty ); | ||
219 | SCAN("Shared_Clean:" , shared_clean ); | ||
220 | #undef SCAN | 227 | #undef SCAN |
228 | } | ||
221 | tp = strchr(buf, '-'); | 229 | tp = strchr(buf, '-'); |
222 | if (tp) { | 230 | if (tp) { |
223 | // We reached next mapping - the line of this form: | 231 | // We reached next mapping - the line of this form: |
224 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME | 232 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME |
225 | 233 | ||
226 | if (cb) { | 234 | char *rwx; |
227 | /* If we have a previous record, there's nothing more | 235 | unsigned long sz; |
228 | * for it, call the callback and clear currec | ||
229 | */ | ||
230 | if (currec.smap_size) | ||
231 | cb(&currec, data); | ||
232 | free(currec.smap_name); | ||
233 | } | ||
234 | memset(&currec, 0, sizeof(currec)); | ||
235 | 236 | ||
236 | *tp = ' '; | 237 | *tp = ' '; |
237 | tp = buf; | 238 | tp = buf; |
238 | currec.smap_start = fast_strtoull_16(&tp); | 239 | sz = fast_strtoull_16(&tp); // start |
239 | currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10; | 240 | sz = (fast_strtoull_16(&tp) - sz) >> 10; // end - start |
240 | 241 | // tp -> "rw-s" string | |
241 | strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); | 242 | rwx = tp; |
242 | |||
243 | // skipping "rw-s FILEOFS M:m INODE " | 243 | // skipping "rw-s FILEOFS M:m INODE " |
244 | tp = skip_whitespace(skip_fields(tp, 4)); | 244 | tp = skip_whitespace(skip_fields(tp, 4)); |
245 | // filter out /dev/something (something != zero) | 245 | // if not a device memory mapped... |
246 | if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) { | 246 | if (memcmp(tp, "/dev/", 5) != 0 // not "/dev/something" |
247 | if (currec.smap_mode[1] == 'w') { | 247 | || strcmp(tp + 5, "zero\n") == 0 // or is "/dev/zero" (which isn't a device) |
248 | currec.mapped_rw = currec.smap_size; | 248 | ) { |
249 | total->mapped_rw += currec.smap_size; | 249 | if (rwx[1] == 'w') |
250 | } else if (currec.smap_mode[1] == '-') { | 250 | sp->mapped_rw += sz; |
251 | currec.mapped_ro = currec.smap_size; | 251 | else if (rwx[0] == 'r' || rwx[2] == 'x') |
252 | total->mapped_ro += currec.smap_size; | 252 | sp->mapped_ro += sz; |
253 | } | 253 | // else: seen "---p" mappings (mmap guard gaps?), |
254 | // do NOT account these as VSZ, they aren't really | ||
254 | } | 255 | } |
255 | |||
256 | if (strcmp(tp, "[stack]\n") == 0) | 256 | if (strcmp(tp, "[stack]\n") == 0) |
257 | total->stack += currec.smap_size; | 257 | sp->stack += sz; |
258 | if (cb) { | ||
259 | p = skip_non_whitespace(tp); | ||
260 | if (p == tp) { | ||
261 | currec.smap_name = xstrdup(" [ anon ]"); | ||
262 | } else { | ||
263 | *p = '\0'; | ||
264 | currec.smap_name = xstrdup(tp); | ||
265 | } | ||
266 | } | ||
267 | total->smap_size += currec.smap_size; | ||
268 | } | 258 | } |
269 | } | 259 | } |
270 | fclose(file); | 260 | fclose(file); |
271 | |||
272 | if (cb) { | ||
273 | if (currec.smap_size) | ||
274 | cb(&currec, data); | ||
275 | free(currec.smap_name); | ||
276 | } | ||
277 | |||
278 | return 0; | ||
279 | } | 261 | } |
280 | #endif | 262 | #endif |
281 | 263 | ||
@@ -370,7 +352,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |||
370 | char *cp, *comm1; | 352 | char *cp, *comm1; |
371 | int tty; | 353 | int tty; |
372 | #if !ENABLE_FEATURE_FAST_TOP | 354 | #if !ENABLE_FEATURE_FAST_TOP |
373 | unsigned long vsz, rss; | 355 | unsigned long long vsz; |
356 | unsigned long rss; | ||
374 | #endif | 357 | #endif |
375 | /* see proc(5) for some details on this */ | 358 | /* see proc(5) for some details on this */ |
376 | strcpy(filename_tail, "stat"); | 359 | strcpy(filename_tail, "stat"); |
@@ -396,7 +379,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |||
396 | "%ld " /* nice */ | 379 | "%ld " /* nice */ |
397 | "%*s %*s " /* timeout, it_real_value */ | 380 | "%*s %*s " /* timeout, it_real_value */ |
398 | "%lu " /* start_time */ | 381 | "%lu " /* start_time */ |
399 | "%lu " /* vsize */ | 382 | "%llu " /* vsize - can be very large */ |
400 | "%lu " /* rss */ | 383 | "%lu " /* rss */ |
401 | # if ENABLE_FEATURE_TOP_SMP_PROCESS | 384 | # if ENABLE_FEATURE_TOP_SMP_PROCESS |
402 | "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ | 385 | "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ |
@@ -449,7 +432,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |||
449 | cp = skip_fields(cp, 2); /* timeout, it_real_value */ | 432 | cp = skip_fields(cp, 2); /* timeout, it_real_value */ |
450 | sp->start_time = fast_strtoul_10(&cp); | 433 | sp->start_time = fast_strtoul_10(&cp); |
451 | /* vsz is in bytes and we want kb */ | 434 | /* vsz is in bytes and we want kb */ |
452 | sp->vsz = fast_strtoul_10(&cp) >> 10; | 435 | sp->vsz = fast_strtoull_10(&cp) >> 10; |
453 | /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ | 436 | /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ |
454 | sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; | 437 | sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; |
455 | # if ENABLE_FEATURE_TOP_SMP_PROCESS | 438 | # if ENABLE_FEATURE_TOP_SMP_PROCESS |
@@ -483,7 +466,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |||
483 | 466 | ||
484 | #if ENABLE_FEATURE_TOPMEM | 467 | #if ENABLE_FEATURE_TOPMEM |
485 | if (flags & PSSCAN_SMAPS) | 468 | if (flags & PSSCAN_SMAPS) |
486 | procps_read_smaps(pid, &sp->smaps, NULL, NULL); | 469 | procps_read_smaps(pid, sp); |
487 | #endif /* TOPMEM */ | 470 | #endif /* TOPMEM */ |
488 | #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS | 471 | #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS |
489 | if (flags & PSSCAN_RUIDGID) { | 472 | if (flags & PSSCAN_RUIDGID) { |
@@ -566,36 +549,45 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |||
566 | return sp; | 549 | return sp; |
567 | } | 550 | } |
568 | 551 | ||
569 | void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) | 552 | int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) |
570 | { | 553 | { |
571 | int sz; | 554 | int sz; |
572 | char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; | 555 | char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; |
573 | 556 | ||
574 | sprintf(filename, "/proc/%u/cmdline", pid); | 557 | sprintf(filename, "/proc/%u/cmdline", pid); |
575 | sz = open_read_close(filename, buf, col - 1); | 558 | sz = open_read_close(filename, buf, col - 1); |
559 | if (sz < 0) | ||
560 | return sz; | ||
576 | if (sz > 0) { | 561 | if (sz > 0) { |
577 | const char *base; | 562 | const char *program_basename; |
578 | int comm_len; | 563 | int comm_len; |
579 | 564 | ||
580 | buf[sz] = '\0'; | 565 | buf[sz] = '\0'; |
581 | while (--sz >= 0 && buf[sz] == '\0') | 566 | while (--sz >= 0 && buf[sz] == '\0') |
582 | continue; | 567 | continue; |
583 | /* Prevent basename("process foo/bar") = "bar" */ | 568 | |
584 | strchrnul(buf, ' ')[0] = '\0'; | 569 | /* Find "program" in "[-][/PATH/TO/]program" */ |
585 | base = bb_basename(buf); /* before we replace argv0's NUL with space */ | 570 | strchrnul(buf, ' ')[0] = '\0'; /* prevent basename("program foo/bar") = "bar" */ |
571 | program_basename = bb_basename(buf[0] == '-' ? buf + 1 : buf); | ||
572 | /* ^^^ note: must do it *before* replacing argv0's NUL with space */ | ||
573 | |||
574 | /* Prevent stuff like this: | ||
575 | * echo 'sleep 999; exit' >`printf '\ec'`; sh ?c | ||
576 | * messing up top and ps output (or worse). | ||
577 | * This also replaces NULs with spaces, converting | ||
578 | * list of NUL-strings into one string. | ||
579 | */ | ||
586 | while (sz >= 0) { | 580 | while (sz >= 0) { |
587 | if ((unsigned char)(buf[sz]) < ' ') | 581 | if ((unsigned char)(buf[sz]) < ' ') |
588 | buf[sz] = ' '; | 582 | buf[sz] = ' '; |
589 | sz--; | 583 | sz--; |
590 | } | 584 | } |
591 | if (base[0] == '-') /* "-sh" (login shell)? */ | ||
592 | base++; | ||
593 | 585 | ||
594 | /* If comm differs from argv0, prepend "{comm} ". | 586 | /* If comm differs from argv0, prepend "{comm} ". |
595 | * It allows to see thread names set by prctl(PR_SET_NAME). | 587 | * It allows to see thread names set by prctl(PR_SET_NAME). |
596 | */ | 588 | */ |
597 | if (!comm) | 589 | if (!comm) |
598 | return; | 590 | return 0; |
599 | comm_len = strlen(comm); | 591 | comm_len = strlen(comm); |
600 | /* Why compare up to comm_len, not COMM_LEN-1? | 592 | /* Why compare up to comm_len, not COMM_LEN-1? |
601 | * Well, some processes rewrite argv, and use _spaces_ there | 593 | * Well, some processes rewrite argv, and use _spaces_ there |
@@ -603,21 +595,24 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) | |||
603 | * I prefer to still treat argv0 "process foo bar" | 595 | * I prefer to still treat argv0 "process foo bar" |
604 | * as 'equal' to comm "process". | 596 | * as 'equal' to comm "process". |
605 | */ | 597 | */ |
606 | if (strncmp(base, comm, comm_len) != 0) { | 598 | if (strncmp(program_basename, comm, comm_len) != 0) { |
607 | comm_len += 3; | 599 | comm_len += 3; |
608 | if (col > comm_len) | 600 | if (col > comm_len) |
609 | memmove(buf + comm_len, buf, col - comm_len); | 601 | memmove(buf + comm_len, buf, col - comm_len); |
610 | snprintf(buf, col, "{%s}", comm); | 602 | snprintf(buf, col, "{%s}", comm); |
611 | if (col <= comm_len) | 603 | if (col <= comm_len) |
612 | return; | 604 | return 0; |
613 | buf[comm_len - 1] = ' '; | 605 | buf[comm_len - 1] = ' '; |
614 | buf[col - 1] = '\0'; | 606 | buf[col - 1] = '\0'; |
615 | } | 607 | } |
616 | } else { | 608 | } else { |
617 | snprintf(buf, col, "[%s]", comm ? comm : "?"); | 609 | snprintf(buf, col, "[%s]", comm ? comm : "?"); |
618 | } | 610 | } |
611 | return 0; | ||
619 | } | 612 | } |
620 | 613 | ||
614 | #endif /* ENABLE_PLATFORM_MINGW32 */ | ||
615 | |||
621 | /* from kernel: | 616 | /* from kernel: |
622 | // pid comm S ppid pgid sid tty_nr tty_pgrp flg | 617 | // pid comm S ppid pgid sid tty_nr tty_pgrp flg |
623 | sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ | 618 | sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ |