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