pmap: new applet. +1k.
No binary (build failed)
—
(log yok)
| @@ -1387,6 +1387,29 @@ enum { COMM_LEN = TASK_COMM_LEN }; | |
| enum { COMM_LEN = 16 }; | |
| # endif | |
| #endif | |
| + | |
| + | struct smaprec { |
| + | unsigned long mapped_rw; |
| + | unsigned long mapped_ro; |
| + | unsigned long shared_clean; |
| + | unsigned long shared_dirty; |
| + | unsigned long private_clean; |
| + | unsigned long private_dirty; |
| + | unsigned long stack; |
| + | unsigned long smap_pss, smap_swap; |
| + | unsigned long smap_size; |
| + | unsigned long smap_start; |
| + | char smap_mode[5]; |
| + | char *smap_name; |
| + | }; |
| + | |
| + | #if !ENABLE_PMAP |
| + | #define procps_read_smaps(pid, total, cb, data) \ |
| + | procps_read_smaps(pid, total) |
| + | #endif |
| + | int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, |
| + | void (*cb)(struct smaprec *, void *), void *data); |
| + | |
| typedef struct procps_status_t { | |
| DIR *dir; | |
| IF_FEATURE_SHOW_THREADS(DIR *task_dir;) | |
| @@ -1415,13 +1438,7 @@ typedef struct procps_status_t { | |
| #endif | |
| unsigned tty_major,tty_minor; | |
| #if ENABLE_FEATURE_TOPMEM | |
| - | unsigned long mapped_rw; |
| - | unsigned long mapped_ro; |
| - | unsigned long shared_clean; |
| - | unsigned long shared_dirty; |
| - | unsigned long private_clean; |
| - | unsigned long private_dirty; |
| - | unsigned long stack; |
| + | struct smaprec smaps; |
| #endif | |
| char state[4]; | |
| /* basename of executable in exec(2), read from /proc/N/stat | |
| @@ -137,12 +137,9 @@ static unsigned long fast_strtoul_16(char **endptr) | |
| *endptr = str; /* We skip trailing space! */ | |
| return n; | |
| } | |
| - | /* TOPMEM uses fast_strtoul_10, so... */ |
| - | # undef ENABLE_FEATURE_FAST_TOP |
| - | # define ENABLE_FEATURE_FAST_TOP 1 |
| #endif | |
| - | #if ENABLE_FEATURE_FAST_TOP |
| + | #if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP |
| /* We cut a lot of corners here for speed */ | |
| static unsigned long fast_strtoul_10(char **endptr) | |
| { | |
| @@ -177,6 +174,111 @@ static char *skip_fields(char *str, int count) | |
| } | |
| #endif | |
| + | #if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP |
| + | int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, |
| + | void (*cb)(struct smaprec *, void *), void *data) |
| + | { |
| + | FILE *file; |
| + | struct smaprec currec; |
| + | char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; |
| + | char buf[PROCPS_BUFSIZE]; |
| + | #if !ENABLE_PMAP |
| + | void (*cb)(struct smaprec *, void *) = NULL; |
| + | void *data = NULL; |
| + | #endif |
| + | |
| + | sprintf(filename, "/proc/%u/smaps", (int)pid); |
| + | |
| + | file = fopen_for_read(filename); |
| + | if (!file) |
| + | return 1; |
| + | |
| + | memset(&currec, 0, sizeof(currec)); |
| + | while (fgets(buf, PROCPS_BUFSIZE, file)) { |
| + | // Each mapping datum has this form: |
| + | // f7d29000-f7d39000 rw-s ADR M:m OFS FILE |
| + | // Size: nnn kB |
| + | // Rss: nnn kB |
| + | // ..... |
| + | |
| + | char *tp = buf, *p; |
| + | |
| + | #define SCAN(S, X) \ |
| + | if (strncmp(tp, S, sizeof(S)-1) == 0) { \ |
| + | tp = skip_whitespace(tp + sizeof(S)-1); \ |
| + | total->X += currec.X = fast_strtoul_10(&tp); \ |
| + | continue; \ |
| + | } |
| + | if (cb) { |
| + | SCAN("Pss:" , smap_pss ); |
| + | SCAN("Swap:" , smap_swap ); |
| + | } |
| + | SCAN("Private_Dirty:", private_dirty); |
| + | SCAN("Private_Clean:", private_clean); |
| + | SCAN("Shared_Dirty:" , shared_dirty ); |
| + | SCAN("Shared_Clean:" , shared_clean ); |
| + | #undef SCAN |
| + | tp = strchr(buf, '-'); |
| + | if (tp) { |
| + | // We reached next mapping - the line of this form: |
| + | // f7d29000-f7d39000 rw-s ADR M:m OFS FILE |
| + | |
| + | if (cb) { |
| + | /* If we have a previous record, there's nothing more |
| + | * for it, call the callback and clear currec |
| + | */ |
| + | if (currec.smap_size) |
| + | cb(&currec, data); |
| + | free(currec.smap_name); |
| + | } |
| + | memset(&currec, 0, sizeof(currec)); |
| + | |
| + | *tp = ' '; |
| + | tp = buf; |
| + | currec.smap_start = fast_strtoul_16(&tp); |
| + | currec.smap_size = (fast_strtoul_16(&tp) - currec.smap_start) >> 10; |
| + | |
| + | strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); |
| + | |
| + | // skipping "rw-s ADR M:m OFS " |
| + | tp = skip_whitespace(skip_fields(tp, 4)); |
| + | // filter out /dev/something (something != zero) |
| + | if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) { |
| + | if (currec.smap_mode[1] == 'w') { |
| + | currec.mapped_rw = currec.smap_size; |
| + | total->mapped_rw += currec.smap_size; |
| + | } else if (currec.smap_mode[1] == '-') { |
| + | currec.mapped_ro = currec.smap_size; |
| + | total->mapped_ro += currec.smap_size; |
| + | } |
| + | } |
| + | |
| + | if (strcmp(tp, "[stack]\n") == 0) |
| + | total->stack += currec.smap_size; |
| + | if (cb) { |
| + | p = skip_non_whitespace(tp); |
| + | if (p == tp) { |
| + | currec.smap_name = xstrdup(" [ anon ]"); |
| + | } else { |
| + | *p = '\0'; |
| + | currec.smap_name = xstrdup(tp); |
| + | } |
| + | } |
| + | total->smap_size += currec.smap_size; |
| + | } |
| + | } |
| + | fclose(file); |
| + | |
| + | if (cb) { |
| + | if (currec.smap_size) |
| + | cb(&currec, data); |
| + | free(currec.smap_name); |
| + | } |
| + | |
| + | return 0; |
| + | } |
| + | #endif |
| + | |
| void BUG_comm_size(void); | |
| procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |
| { | |
| @@ -365,54 +467,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |
| } | |
| #if ENABLE_FEATURE_TOPMEM | |
| - | if (flags & (PSSCAN_SMAPS)) { |
| - | FILE *file; |
| - | |
| - | strcpy(filename_tail, "smaps"); |
| - | file = fopen_for_read(filename); |
| - | if (file) { |
| - | while (fgets(buf, sizeof(buf), file)) { |
| - | unsigned long sz; |
| - | char *tp; |
| - | char w; |
| - | #define SCAN(str, name) \ |
| - | if (strncmp(buf, str, sizeof(str)-1) == 0) { \ |
| - | tp = skip_whitespace(buf + sizeof(str)-1); \ |
| - | sp->name += fast_strtoul_10(&tp); \ |
| - | continue; \ |
| - | } |
| - | SCAN("Shared_Clean:" , shared_clean ); |
| - | SCAN("Shared_Dirty:" , shared_dirty ); |
| - | SCAN("Private_Clean:", private_clean); |
| - | SCAN("Private_Dirty:", private_dirty); |
| - | #undef SCAN |
| - | // f7d29000-f7d39000 rw-s ADR M:m OFS FILE |
| - | tp = strchr(buf, '-'); |
| - | if (tp) { |
| - | *tp = ' '; |
| - | tp = buf; |
| - | sz = fast_strtoul_16(&tp); /* start */ |
| - | sz = (fast_strtoul_16(&tp) - sz) >> 10; /* end - start */ |
| - | // tp -> "rw-s" string |
| - | w = tp[1]; |
| - | // skipping "rw-s ADR M:m OFS " |
| - | tp = skip_whitespace(skip_fields(tp, 4)); |
| - | // filter out /dev/something (something != zero) |
| - | if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) { |
| - | if (w == 'w') { |
| - | sp->mapped_rw += sz; |
| - | } else if (w == '-') { |
| - | sp->mapped_ro += sz; |
| - | } |
| - | } |
| - | //else printf("DROPPING %s (%s)\n", buf, tp); |
| - | if (strcmp(tp, "[stack]\n") == 0) |
| - | sp->stack += sz; |
| - | } |
| - | } |
| - | fclose(file); |
| - | } |
| - | } |
| + | if (flags & PSSCAN_SMAPS) |
| + | procps_read_smaps(pid, &sp->smaps, NULL, NULL); |
| #endif /* TOPMEM */ | |
| #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS | |
| if (flags & PSSCAN_RUIDGID) { | |
| @@ -0,0 +1,111 @@ | |
| + | /* |
| + | * pmap implementation for busybox |
| + | * |
| + | * Copyright (C) 2010 Nokia Corporation. All rights reserved. |
| + | * Written by Alexander Shishkin <[email protected]> |
| + | * |
| + | * Licensed under GPLv2 or later, see the LICENSE file in this source tree |
| + | * for details. |
| + | */ |
| + | |
| + | //applet:IF_PMAP(APPLET(pmap, _BB_DIR_USR_BIN, _BB_SUID_DROP)) |
| + | //kbuild:lib-$(CONFIG_PMAP) += pmap.o |
| + | |
| + | //config:config PMAP |
| + | //config: bool "pmap" |
| + | //config: default y |
| + | //config: help |
| + | //config: Display processes' memory mappings. |
| + | |
| + | //usage:#define pmap_trivial_usage |
| + | //usage: "[-x][-q] PID" |
| + | //usage:#define pmap_full_usage "\n\n" |
| + | //usage: "Display detailed precesses' memory usage\n" |
| + | //usage: "\nOptions:" |
| + | //usage: "\n -x show details" |
| + | //usage: "\n -q quiet" |
| + | |
| + | #include "libbb.h" |
| + | |
| + | #if ULONG_MAX == 0xffffffff |
| + | # define TABS "\t" |
| + | # define AFMT "8" |
| + | # define DASHES "" |
| + | #else |
| + | # define TABS "\t\t" |
| + | # define AFMT "16" |
| + | # define DASHES "--------" |
| + | #endif |
| + | |
| + | enum { |
| + | OPT_x = 1 << 0, |
| + | OPT_q = 1 << 1, |
| + | }; |
| + | |
| + | static void print_smaprec(struct smaprec *currec, void *data) |
| + | { |
| + | unsigned opt = (unsigned)data; |
| + | |
| + | printf("%0" AFMT "lx ", currec->smap_start); |
| + | |
| + | if (opt & OPT_x) |
| + | printf("%7lu %7lu %7lu %7lu ", |
| + | currec->smap_size, |
| + | currec->smap_pss, |
| + | currec->private_dirty, |
| + | currec->smap_swap); |
| + | else |
| + | printf("%7luK", currec->smap_size); |
| + | |
| + | printf(" %.4s %s\n", currec->smap_mode, currec->smap_name); |
| + | } |
| + | |
| + | static int procps_get_maps(pid_t pid, unsigned opt) |
| + | { |
| + | struct smaprec total; |
| + | int ret; |
| + | char buf[256]; |
| + | |
| + | read_cmdline(buf, sizeof(buf), pid, "no such process"); |
| + | printf("%u: %s\n", (int)pid, buf); |
| + | |
| + | if (!(opt & OPT_q) && (opt & OPT_x)) |
| + | puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping"); |
| + | |
| + | memset(&total, 0, sizeof(total)); |
| + | |
| + | ret = procps_read_smaps(pid, &total, print_smaprec, (void*)opt); |
| + | if (ret) |
| + | return ret; |
| + | |
| + | if (!(opt & OPT_q)) { |
| + | if (opt & OPT_x) |
| + | printf("--------" DASHES " ------ ------ ------ ------\n" |
| + | "total" TABS " %7lu %7lu %7lu %7lu\n", |
| + | total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); |
| + | else |
| + | printf("mapped: %luK\n", total.smap_size); |
| + | } |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | int pmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| + | int pmap_main(int argc UNUSED_PARAM, char **argv) |
| + | { |
| + | unsigned opts; |
| + | int ret; |
| + | |
| + | opts = getopt32(argv, "xq"); |
| + | argv += optind; |
| + | |
| + | ret = 0; |
| + | while (*argv) { |
| + | pid_t pid = xatoi_positive(*argv++); |
| + | /* GNU pmap returns 42 if any of the pids failed */ |
| + | if (procps_get_maps(pid, opts) != 0) |
| + | ret = 42; |
| + | } |
| + | |
| + | return ret; |
| + | } |
| @@ -942,20 +942,20 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |
| } | |
| #if ENABLE_FEATURE_TOPMEM | |
| else { /* TOPMEM */ | |
| - | if (!(p->mapped_ro | p->mapped_rw)) |
| + | if (!(p->smaps.mapped_ro | p->smaps.mapped_rw)) |
| continue; /* kernel threads are ignored */ | |
| n = ntop; | |
| /* No bug here - top and topmem are the same */ | |
| top = xrealloc_vector(topmem, 6, ntop++); | |
| strcpy(topmem[n].comm, p->comm); | |
| topmem[n].pid = p->pid; | |
| - | topmem[n].vsz = p->mapped_rw + p->mapped_ro; |
| - | topmem[n].vszrw = p->mapped_rw; |
| - | topmem[n].rss_sh = p->shared_clean + p->shared_dirty; |
| - | topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh; |
| - | topmem[n].dirty = p->private_dirty + p->shared_dirty; |
| - | topmem[n].dirty_sh = p->shared_dirty; |
| - | topmem[n].stack = p->stack; |
| + | topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro; |
| + | topmem[n].vszrw = p->smaps.mapped_rw; |
| + | topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty; |
| + | topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh; |
| + | topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty; |
| + | topmem[n].dirty_sh = p->smaps.shared_dirty; |
| + | topmem[n].stack = p->smaps.stack; |
| } | |
| #endif | |
| } /* end of "while we read /proc" */ | |