diff options
Diffstat (limited to 'libbb')
| -rw-r--r-- | libbb/procps.c | 130 |
1 files changed, 48 insertions, 82 deletions
diff --git a/libbb/procps.c b/libbb/procps.c index 2cdfce42a..3256fafc5 100644 --- a/libbb/procps.c +++ b/libbb/procps.c | |||
| @@ -109,7 +109,7 @@ void FAST_FUNC free_procps_scan(procps_status_t* sp) | |||
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | #if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP | 111 | #if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP |
| 112 | static unsigned long long fast_strtoull_16(char **endptr) | 112 | unsigned long long FAST_FUNC fast_strtoull_16(char **endptr) |
| 113 | { | 113 | { |
| 114 | unsigned char c; | 114 | unsigned char c; |
| 115 | char *str = *endptr; | 115 | char *str = *endptr; |
| @@ -130,7 +130,7 @@ static unsigned long long fast_strtoull_16(char **endptr) | |||
| 130 | 130 | ||
| 131 | #if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP | 131 | #if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP |
| 132 | /* We cut a lot of corners here for speed */ | 132 | /* We cut a lot of corners here for speed */ |
| 133 | static unsigned long fast_strtoul_10(char **endptr) | 133 | unsigned long FAST_FUNC fast_strtoul_10(char **endptr) |
| 134 | { | 134 | { |
| 135 | unsigned char c; | 135 | unsigned char c; |
| 136 | char *str = *endptr; | 136 | char *str = *endptr; |
| @@ -159,7 +159,7 @@ static unsigned long long fast_strtoull_10(char **endptr) | |||
| 159 | return n; | 159 | return n; |
| 160 | } | 160 | } |
| 161 | # else | 161 | # else |
| 162 | # define fast_strtoull_10(endptr) fast_strtoul_10(endptr) | 162 | # define fast_strtoull_10(endptr) fast_strtoul_10(endptr) |
| 163 | # endif | 163 | # endif |
| 164 | 164 | ||
| 165 | # if ENABLE_FEATURE_FAST_TOP | 165 | # if ENABLE_FEATURE_FAST_TOP |
| @@ -173,7 +173,7 @@ static long fast_strtol_10(char **endptr) | |||
| 173 | } | 173 | } |
| 174 | # endif | 174 | # endif |
| 175 | 175 | ||
| 176 | static char *skip_fields(char *str, int count) | 176 | char* FAST_FUNC skip_fields(char *str, int count) |
| 177 | { | 177 | { |
| 178 | do { | 178 | do { |
| 179 | while (*str++ != ' ') | 179 | while (*str++ != ' ') |
| @@ -184,35 +184,25 @@ static char *skip_fields(char *str, int count) | |||
| 184 | } | 184 | } |
| 185 | #endif | 185 | #endif |
| 186 | 186 | ||
| 187 | #if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP | 187 | #if ENABLE_FEATURE_TOPMEM |
| 188 | static char* skip_whitespace_if_prefixed_with(char *buf, const char *prefix) | 188 | static NOINLINE void procps_read_smaps(pid_t pid, procps_status_t *sp) |
| 189 | { | 189 | { |
| 190 | char *tp = is_prefixed_with(buf, prefix); | 190 | // There is A LOT of /proc/PID/smaps data on a big system. |
| 191 | if (tp) { | 191 | // Optimize this for speed, makes "top -m" faster. |
| 192 | tp = skip_whitespace(tp); | 192 | //TODO large speedup: |
| 193 | } | 193 | //read /proc/PID/smaps_rollup (cumulative stats of all mappings, much faster) |
| 194 | return tp; | 194 | //and /proc/PID/maps to get mapped_ro and mapped_rw (IOW: VSZ,VSZRW) |
| 195 | } | ||
| 196 | 195 | ||
| 197 | int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, | ||
| 198 | void (*cb)(struct smaprec *, void *), void *data) | ||
| 199 | { | ||
| 200 | FILE *file; | 196 | FILE *file; |
| 201 | struct smaprec currec; | ||
| 202 | char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; | 197 | char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; |
| 203 | char buf[PROCPS_BUFSIZE]; | 198 | char buf[PROCPS_BUFSIZE]; |
| 204 | #if !ENABLE_PMAP | ||
| 205 | void (*cb)(struct smaprec *, void *) = NULL; | ||
| 206 | void *data = NULL; | ||
| 207 | #endif | ||
| 208 | 199 | ||
| 209 | sprintf(filename, "/proc/%u/smaps", (int)pid); | 200 | sprintf(filename, "/proc/%u/smaps", (int)pid); |
| 210 | 201 | ||
| 211 | file = fopen_for_read(filename); | 202 | file = fopen_for_read(filename); |
| 212 | if (!file) | 203 | if (!file) |
| 213 | return 1; | 204 | return; |
| 214 | 205 | ||
| 215 | memset(&currec, 0, sizeof(currec)); | ||
| 216 | while (fgets(buf, PROCPS_BUFSIZE, file)) { | 206 | while (fgets(buf, PROCPS_BUFSIZE, file)) { |
| 217 | // Each mapping datum has this form: | 207 | // Each mapping datum has this form: |
| 218 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME | 208 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME |
| @@ -220,80 +210,53 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, | |||
| 220 | // Rss: nnn kB | 210 | // Rss: nnn kB |
| 221 | // ..... | 211 | // ..... |
| 222 | 212 | ||
| 223 | char *tp, *p; | 213 | char *tp; |
| 224 | 214 | ||
| 215 | if (buf[0] == 'S' || buf[0] == 'P') { | ||
| 225 | #define SCAN(S, X) \ | 216 | #define SCAN(S, X) \ |
| 226 | if ((tp = skip_whitespace_if_prefixed_with(buf, S)) != NULL) { \ | 217 | if (memcmp(buf, S, sizeof(S)-1) == 0) { \ |
| 227 | total->X += currec.X = fast_strtoul_10(&tp); \ | 218 | tp = skip_whitespace(buf + sizeof(S)-1); \ |
| 228 | continue; \ | 219 | sp->X += fast_strtoul_10(&tp); \ |
| 229 | } | 220 | continue; \ |
| 230 | if (cb) { | 221 | } |
| 231 | SCAN("Pss:" , smap_pss ); | 222 | SCAN("Private_Dirty:", private_dirty) |
| 232 | SCAN("Swap:" , smap_swap ); | 223 | SCAN("Private_Clean:", private_clean) |
| 233 | } | 224 | SCAN("Shared_Dirty:" , shared_dirty ) |
| 234 | SCAN("Private_Dirty:", private_dirty); | 225 | SCAN("Shared_Clean:" , shared_clean ) |
| 235 | SCAN("Private_Clean:", private_clean); | ||
| 236 | SCAN("Shared_Dirty:" , shared_dirty ); | ||
| 237 | SCAN("Shared_Clean:" , shared_clean ); | ||
| 238 | #undef SCAN | 226 | #undef SCAN |
| 227 | } | ||
| 239 | tp = strchr(buf, '-'); | 228 | tp = strchr(buf, '-'); |
| 240 | if (tp) { | 229 | if (tp) { |
| 241 | // We reached next mapping - the line of this form: | 230 | // We reached next mapping - the line of this form: |
| 242 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME | 231 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME |
| 243 | 232 | ||
| 244 | if (cb) { | 233 | char *rwx; |
| 245 | /* If we have a previous record, there's nothing more | 234 | unsigned long sz; |
| 246 | * for it, call the callback and clear currec | ||
| 247 | */ | ||
| 248 | if (currec.smap_size) | ||
| 249 | cb(&currec, data); | ||
| 250 | free(currec.smap_name); | ||
| 251 | } | ||
| 252 | memset(&currec, 0, sizeof(currec)); | ||
| 253 | 235 | ||
| 254 | *tp = ' '; | 236 | *tp = ' '; |
| 255 | tp = buf; | 237 | tp = buf; |
| 256 | currec.smap_start = fast_strtoull_16(&tp); | 238 | sz = fast_strtoull_16(&tp); // start |
| 257 | currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10; | 239 | sz = (fast_strtoull_16(&tp) - sz) >> 10; // end - start |
| 258 | 240 | // tp -> "rw-s" string | |
| 259 | strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); | 241 | rwx = tp; |
| 260 | |||
| 261 | // skipping "rw-s FILEOFS M:m INODE " | 242 | // skipping "rw-s FILEOFS M:m INODE " |
| 262 | tp = skip_whitespace(skip_fields(tp, 4)); | 243 | tp = skip_whitespace(skip_fields(tp, 4)); |
| 263 | // filter out /dev/something (something != zero) | 244 | // if not a device memory mapped... |
| 264 | if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) { | 245 | if (memcmp(tp, "/dev/", 5) != 0 // not "/dev/something" |
| 265 | if (currec.smap_mode[1] == 'w') { | 246 | || strcmp(tp + 5, "zero\n") == 0 // or is "/dev/zero" (which isn't a device) |
| 266 | currec.mapped_rw = currec.smap_size; | 247 | ) { |
| 267 | total->mapped_rw += currec.smap_size; | 248 | if (rwx[1] == 'w') |
| 268 | } else if (currec.smap_mode[1] == '-') { | 249 | sp->mapped_rw += sz; |
| 269 | currec.mapped_ro = currec.smap_size; | 250 | else if (rwx[0] == 'r' || rwx[2] == 'x') |
| 270 | total->mapped_ro += currec.smap_size; | 251 | sp->mapped_ro += sz; |
| 271 | } | 252 | // else: seen "---p" mappings (mmap guard gaps?), |
| 253 | // do NOT account these as VSZ, they aren't really | ||
| 272 | } | 254 | } |
| 273 | |||
| 274 | if (strcmp(tp, "[stack]\n") == 0) | 255 | if (strcmp(tp, "[stack]\n") == 0) |
| 275 | total->stack += currec.smap_size; | 256 | sp->stack += sz; |
| 276 | if (cb) { | ||
| 277 | p = skip_non_whitespace(tp); | ||
| 278 | if (p == tp) { | ||
| 279 | currec.smap_name = xstrdup(" [ anon ]"); | ||
| 280 | } else { | ||
| 281 | *p = '\0'; | ||
| 282 | currec.smap_name = xstrdup(tp); | ||
| 283 | } | ||
| 284 | } | ||
| 285 | total->smap_size += currec.smap_size; | ||
| 286 | } | 257 | } |
| 287 | } | 258 | } |
| 288 | fclose(file); | 259 | fclose(file); |
| 289 | |||
| 290 | if (cb) { | ||
| 291 | if (currec.smap_size) | ||
| 292 | cb(&currec, data); | ||
| 293 | free(currec.smap_name); | ||
| 294 | } | ||
| 295 | |||
| 296 | return 0; | ||
| 297 | } | 260 | } |
| 298 | #endif | 261 | #endif |
| 299 | 262 | ||
| @@ -502,7 +465,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |||
| 502 | 465 | ||
| 503 | #if ENABLE_FEATURE_TOPMEM | 466 | #if ENABLE_FEATURE_TOPMEM |
| 504 | if (flags & PSSCAN_SMAPS) | 467 | if (flags & PSSCAN_SMAPS) |
| 505 | procps_read_smaps(pid, &sp->smaps, NULL, NULL); | 468 | procps_read_smaps(pid, sp); |
| 506 | #endif /* TOPMEM */ | 469 | #endif /* TOPMEM */ |
| 507 | #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS | 470 | #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS |
| 508 | if (flags & PSSCAN_RUIDGID) { | 471 | if (flags & PSSCAN_RUIDGID) { |
| @@ -585,13 +548,15 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |||
| 585 | return sp; | 548 | return sp; |
| 586 | } | 549 | } |
| 587 | 550 | ||
| 588 | void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) | 551 | int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) |
| 589 | { | 552 | { |
| 590 | int sz; | 553 | int sz; |
| 591 | char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; | 554 | char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; |
| 592 | 555 | ||
| 593 | sprintf(filename, "/proc/%u/cmdline", pid); | 556 | sprintf(filename, "/proc/%u/cmdline", pid); |
| 594 | sz = open_read_close(filename, buf, col - 1); | 557 | sz = open_read_close(filename, buf, col - 1); |
| 558 | if (sz < 0) | ||
| 559 | return sz; | ||
| 595 | if (sz > 0) { | 560 | if (sz > 0) { |
| 596 | const char *base; | 561 | const char *base; |
| 597 | int comm_len; | 562 | int comm_len; |
| @@ -614,7 +579,7 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) | |||
| 614 | * It allows to see thread names set by prctl(PR_SET_NAME). | 579 | * It allows to see thread names set by prctl(PR_SET_NAME). |
| 615 | */ | 580 | */ |
| 616 | if (!comm) | 581 | if (!comm) |
| 617 | return; | 582 | return 0; |
| 618 | comm_len = strlen(comm); | 583 | comm_len = strlen(comm); |
| 619 | /* Why compare up to comm_len, not COMM_LEN-1? | 584 | /* Why compare up to comm_len, not COMM_LEN-1? |
| 620 | * Well, some processes rewrite argv, and use _spaces_ there | 585 | * Well, some processes rewrite argv, and use _spaces_ there |
| @@ -628,13 +593,14 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) | |||
| 628 | memmove(buf + comm_len, buf, col - comm_len); | 593 | memmove(buf + comm_len, buf, col - comm_len); |
| 629 | snprintf(buf, col, "{%s}", comm); | 594 | snprintf(buf, col, "{%s}", comm); |
| 630 | if (col <= comm_len) | 595 | if (col <= comm_len) |
| 631 | return; | 596 | return 0; |
| 632 | buf[comm_len - 1] = ' '; | 597 | buf[comm_len - 1] = ' '; |
| 633 | buf[col - 1] = '\0'; | 598 | buf[col - 1] = '\0'; |
| 634 | } | 599 | } |
| 635 | } else { | 600 | } else { |
| 636 | snprintf(buf, col, "[%s]", comm ? comm : "?"); | 601 | snprintf(buf, col, "[%s]", comm ? comm : "?"); |
| 637 | } | 602 | } |
| 603 | return 0; | ||
| 638 | } | 604 | } |
| 639 | 605 | ||
| 640 | /* from kernel: | 606 | /* from kernel: |
