pmap is a tool used to look at processes' memory maps, normally found
in procps package. It provides more readable and easily sortable output
(one line per mapping) from  maps/smaps files in /proc/PID/.  This would
help in debugging memory usage issues, especially on devices where lots
of typing is not a viable option.

This patch does'n implement -d and -A command line options of GNU pmap,
since those are not that must have features and I was afraid of going
blind from looking at its code.

The implementation takes smaps scanning part out of procps_scan() function
and moves it into procps_read_smaps(), which does more detailed processing
of a single PID's smaps data.

Signed-off-by: Alexander Shishkin <[email protected]>
---
 include/applets.src.h |    1 +
 include/libbb.h       |   27 +++++++--
 include/usage.src.h   |    8 +++
 libbb/procps.c        |  146 ++++++++++++++++++++++++++++++++-----------------
 procps/Config.src     |    6 ++
 procps/Kbuild.src     |    1 +
 procps/pmap.c         |   90 ++++++++++++++++++++++++++++++
 procps/top.c          |   20 ++++---
 8 files changed, 234 insertions(+), 65 deletions(-)
 create mode 100644 procps/pmap.c

diff --git a/include/applets.src.h b/include/applets.src.h
index 195598f..4227ca7 100644
--- a/include/applets.src.h
+++ b/include/applets.src.h
@@ -289,6 +289,7 @@ IF_PING6(APPLET(ping6, _BB_DIR_BIN, _BB_SUID_MAYBE))
 IF_PIPE_PROGRESS(APPLET(pipe_progress, _BB_DIR_BIN, _BB_SUID_DROP))
 IF_PIVOT_ROOT(APPLET(pivot_root, _BB_DIR_SBIN, _BB_SUID_DROP))
 IF_PKILL(APPLET_ODDNAME(pkill, pgrep, _BB_DIR_USR_BIN, _BB_SUID_DROP, pkill))
+IF_PMAP(APPLET(pmap, _BB_DIR_USR_BIN, _BB_SUID_DROP))
 IF_POPMAILDIR(APPLET(popmaildir, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
 IF_HALT(APPLET_ODDNAME(poweroff, halt, _BB_DIR_SBIN, _BB_SUID_DROP, poweroff))
 IF_PRINTENV(APPLET(printenv, _BB_DIR_BIN, _BB_SUID_DROP))
diff --git a/include/libbb.h b/include/libbb.h
index c043506..85fcbb0 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1381,6 +1381,25 @@ 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 pss, swap;
+       unsigned long size;
+       unsigned long start;
+       char mode[5];
+       char *name;
+};
+
+int 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;)
@@ -1409,13 +1428,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
diff --git a/include/usage.src.h b/include/usage.src.h
index 9b326ee..fd19b42 100644
--- a/include/usage.src.h
+++ b/include/usage.src.h
@@ -3208,6 +3208,14 @@ INSERT
      "\n       -s      Match session ID (0 for current)" \
      "\n       -P      Match parent process ID" \
 
+#define pmap_trivial_usage \
+       "[-x][-q] PID"
+#define pmap_full_usage "\n\n" \
+       "Display detailed precesses' memory usage\n" \
+       "\nOptions:" \
+       "\n     -x              show details" \
+       "\n     -q              quiet" \
+
 #define popmaildir_trivial_usage \
        "[OPTIONS] MAILDIR [CONN_HELPER ARGS]"
 #define popmaildir_full_usage "\n\n" \
diff --git a/libbb/procps.c b/libbb/procps.c
index 48e60a7..3494002 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -170,13 +170,105 @@ static char *skip_fields(char *str, int count)
 {
        do {
                while (*str++ != ' ')
-                       continue;
-               /* we found a space char, str points after it */
+                       ;
        } while (--count);
        return str;
 }
 #endif
 
+#ifdef ENABLE_FEATURE_TOPMEM
+int procps_read_smaps(pid_t pid, struct smaprec *total,
+                     void (*cb)(struct smaprec *, void *), void *data)
+{
+       char filename[FILENAME_MAX];
+       FILE *file;
+       char buf[PROCPS_BUFSIZE];
+       struct smaprec currec = { .size = 0, .name = NULL };
+
+       snprintf(filename, FILENAME_MAX, "/proc/%d/smaps", pid);
+
+       file = fopen_for_read(filename);
+       if (!file)
+               return EXIT_FAILURE;
+
+       while (fgets(buf, sizeof buf , file)) {
+               char *tp = buf, *p;
+
+#define SCAN(X) \
+               if (!strncasecmp(tp, #X ":", sizeof #X)) {              \
+                       tp = skip_whitespace(tp + sizeof #X);           \
+                       total->X += currec.X += fast_strtoul_10(&tp);   \
+                       continue;                                       \
+               }
+
+               SCAN(pss);
+               SCAN(swap);
+               SCAN(private_dirty);
+               SCAN(private_clean);
+               SCAN(shared_dirty);
+               SCAN(shared_clean);
+#undef SCAN
+
+               // f7d29000-f7d39000 rw-s ADR M:m OFS FILE
+               tp = strchr(buf, '-');
+               if (tp) {
+                       /*
+                        * if we have a previous record, there's nothing more
+                        * for it, call the callback and clear currec
+                        */
+                       if (currec.size) {
+                               if (cb)
+                                       cb(&currec, data);
+                               free(currec.name);
+                               memset(&currec, 0, sizeof currec);
+                       }
+
+                       *tp = ' ';
+                       tp = buf;
+                       currec.start = fast_strtoul_16(&tp);
+                       currec.size = (fast_strtoul_16(&tp) -
+                                      currec.start) >> 10; /* end - start */
+
+                       strncpy(currec.mode, tp, 4);
+
+                       // 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.mode[1] == 'w') {
+                                       total->mapped_rw +=
+                                               currec.mapped_rw += currec.size;
+                               } else if (currec.mode[1] == '-') {
+                                       total->mapped_ro +=
+                                               currec.mapped_ro += currec.size;
+                               }
+                       }
+
+                       if (strcmp(tp, "[stack]\n") == 0)
+                               total->stack += currec.size;
+                       p = skip_non_whitespace(tp);
+                       if (p == tp)
+                               currec.name = xstrdup("  [ anon ]");
+                       else {
+                               *p = '\0';
+                               currec.name = xstrdup(tp);
+                       }
+
+
+                       total->size += currec.size;
+               }
+       }
+
+       if (cb && currec.size)
+               cb(&currec, data);
+       free(currec.name);
+
+       fclose(file);
+
+       return EXIT_SUCCESS;
+}
+#endif
+
 void BUG_comm_size(void);
 procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
 {
@@ -365,54 +457,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) {
diff --git a/procps/Config.src b/procps/Config.src
index 1ff6dfd..99222e7 100644
--- a/procps/Config.src
+++ b/procps/Config.src
@@ -81,6 +81,12 @@ config FEATURE_PIDOF_OMIT
          The special pid %PPID can be used to name the parent process
          of the pidof, in other words the calling shell or shell script.
 
+config PMAP
+       bool "pmap"
+       default y
+       help
+         Display processes' memory mappings.
+
 config PKILL
        bool "pkill"
        default y
diff --git a/procps/Kbuild.src b/procps/Kbuild.src
index c41f12b..f2b1b34 100644
--- a/procps/Kbuild.src
+++ b/procps/Kbuild.src
@@ -15,6 +15,7 @@ lib-$(CONFIG_NMETER)    += nmeter.o
 lib-$(CONFIG_PGREP)    += pgrep.o
 lib-$(CONFIG_PKILL)    += pgrep.o
 lib-$(CONFIG_PIDOF)    += pidof.o
+lib-$(CONFIG_PMAP)     += pmap.o
 lib-$(CONFIG_PS)       += ps.o
 lib-$(CONFIG_RENICE)   += renice.o
 lib-$(CONFIG_BB_SYSCTL)        += sysctl.o
diff --git a/procps/pmap.c b/procps/pmap.c
new file mode 100644
index 0000000..8610dd4
--- /dev/null
+++ b/procps/pmap.c
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#include "libbb.h"
+
+#define OPT_X  1
+#define OPT_Q  2
+
+#if ULONG_MAX == 0xffffffff
+#define TABS "\t"
+#define AFMT "8"
+#define DASHES ""
+#else
+#define TABS "\t\t"
+#define AFMT "16"
+#define DASHES "--------"
+#endif
+
+static void print_smaprec(struct smaprec *currec, void *data)
+{
+       unsigned int opt = *(unsigned int *)data;
+
+       printf("%0" AFMT "lx ", currec->start);
+
+       if (opt & OPT_X)
+               printf("%7lu %7lu %7lu %7lu ",
+                      currec->size, currec->pss,
+                      currec->shared_dirty + currec->private_dirty,
+                      currec->swap);
+       else
+               printf("%7luK", currec->size);
+
+       printf(" %.4s  %s\n", currec->mode, currec->name);
+}
+
+static int procps_get_maps(pid_t pid, unsigned int opt)
+{
+       char buf[BUFSIZ];
+       struct smaprec total;
+       int ret;
+
+       read_cmdline(buf, BUFSIZ, pid, "no such process");
+       printf("%d:   %s\n", 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, &opt);
+       if (ret)
+               return EXIT_FAILURE;
+
+       if (!(opt & OPT_Q) && (opt & OPT_X))
+               printf("--------" DASHES "  ------  ------  ------  ------\n"
+                      "total" TABS " %7lu %7lu %7lu %7lu\n",
+                      total.size, total.pss,
+                      total.shared_dirty + total.private_dirty, total.swap);
+       else if (!(opt & OPT_Q))
+               printf(" total" TABS "%7luK\n", total.size);
+
+       return EXIT_SUCCESS;
+}
+
+int pmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pmap_main(int argc, char **argv)
+{
+       unsigned int opt;
+       pid_t pid;
+       int ret = 0;
+
+       opt = getopt32(argv, "xq");
+
+       for (argv += optind; argv && *argv; argv++) {
+               pid = bb_strtou(*argv, NULL, 10);
+               ret += !procps_get_maps(pid, opt);
+       }
+
+       /* GNU pmap returns 42 if any of the pids failed */
+       return (ret == argc - optind) ? EXIT_SUCCESS : 42;
+}
+
diff --git a/procps/top.c b/procps/top.c
index ec84374..040e156 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -942,20 +942,24 @@ 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" */
-- 
1.7.1

_______________________________________________
busybox mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to