On Tuesday 17 August 2010 12:05, Alexander Shishkin wrote:
> On Sun, Jul 31, 2010 at 01:23:54 +0300, Alexander Shishkin wrote:
> > 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.
> 
> Ping.

+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;
+};

Can you prefix new field names with smap_ ?
Grepping fails for field named simply "name" - too many
such names already.


                while (*str++ != ' ')
-                       continue;
-               /* we found a space char, str points after it */
+                       ;

Why this change?


+#ifdef ENABLE_FEATURE_TOPMEM

This should be #if, not #ifdef. Also, you need to add "|| ENABLE_PMAP"


+       RESERVE_CONFIG_BUFFER(filename, FILENAME_MAX);
+       RESERVE_CONFIG_BUFFER(buf, PROCPS_BUFSIZE);
+
+       snprintf(filename, FILENAME_MAX, "/proc/%d/smaps", pid);

It's better to use smaller buffer for filename.


+#define SCAN(X) \
+               if (strncasecmp(tp, #X ":", sizeof #X) == 0) {       \
+                       tp = skip_whitespace(tp + sizeof(#X));       \
+                       total->X += currec.X = fast_strtoul_10(&tp); \
+                       continue;                                    \
+               }
+
+               SCAN(pss);
+               SCAN(swap);

strncasecmp is slower than strncmp. You trade a small convenience
in calling the macro for accumulated slowness in top for _every_
_line_ of smaps!


+                       if (p == tp)
+                               currec.name = xstrdup("  [ anon ]");
+                       else {
+                               *p = '\0';
+                               currec.name = xstrdup(tp);
+                       }

This allocating/freeing of name field will slow down top,
without any reason to do so.


+       opt = getopt32(argv, "xq");
+
+       for (argv += optind; argv && *argv; argv++) {

argv is never NULL, no need to check for that.


+       RESERVE_CONFIG_BUFFER(buf, BUFSIZ);
a
+       read_cmdline(buf, BUFSIZ, pid, "no such process");
+       printf("%d:   %s\n", pid, buf);

standard pmap does not show the whole command line, only process name.
The output format is different too.


+       if (!(opt & OPT_Q) && (opt & OPT_X))
+               printf("--------" DASHES "  ------  ------  ------  ------\n"
+                      "total" TABS " %7lu %7lu %7lu %7lu\n",
+                      total.size, total.pss, total.private_dirty, total.swap);
+       else if (!(opt & OPT_Q))
+               printf(" total" TABS "%7luK\n", total.size);

!(opt & OPT_Q) is tested twice.


And now, run testing. I ran top, pressed 's' and then pressed
and kept space down. Top leaks memory:

Mem total:2054936 anon:191364 map:50604 free:583520
 slab:118472 buf:133744 cache:1006420 dirty:20 write:0
Swap total:131068 free:131068
  PID   VSZ*VSZRW   RSS (SHR) DIRTY (SHR) STACK COMMAND
 3616  202m  165m 67904  2824 47876    64   132 /usr/app/firefox-3.6/firefox-bin
24722 45092 44236 44292     0 44116     0   132 ./busybox top      
<=======================
 3075  104m 36972 56988 11944 38176     0  2368 kmail -caption KMail -icon 
kmail -miniicon kmail
 2053 17080 12368 14784  1460 11232     0   132 X :0
...

I'm not impressed.
Try attached.

-- 
vda
diff -ad -urpN busybox.2/include/libbb.h busybox.3/include/libbb.h
--- busybox.2/include/libbb.h	2010-08-22 05:38:55.000000000 +0200
+++ busybox.3/include/libbb.h	2010-08-24 08:54:23.000000000 +0200
@@ -1387,6 +1387,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 smap_pss, smap_swap;
+	unsigned long smap_size;
+	unsigned long smap_start;
+	char smap_mode[5];
+	char *smap_name;
+};
+
+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 +1434,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 -ad -urpN busybox.2/libbb/procps.c busybox.3/libbb/procps.c
--- busybox.2/libbb/procps.c	2010-08-16 20:44:55.000000000 +0200
+++ busybox.3/libbb/procps.c	2010-08-24 09:41:02.000000000 +0200
@@ -177,6 +177,105 @@ static char *skip_fields(char *str, int 
 }
 #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];
+
+	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;                                    \
+		}
+		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 we have a previous record, there's nothing more
+			 * for it, call the callback and clear currec
+			 */
+			if (cb) {
+				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;
+			p = skip_non_whitespace(tp);
+			if (cb) {
+				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 +464,8 @@ procps_status_t* FAST_FUNC procps_scan(p
 		}
 
 #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 -ad -urpN busybox.2/procps/pmap.c busybox.3/procps/pmap.c
--- busybox.2/procps/pmap.c	1970-01-01 01:00:00.000000000 +0100
+++ busybox.3/procps/pmap.c	2010-08-24 09:45:39.000000000 +0200
@@ -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;
+}
diff -ad -urpN busybox.2/procps/top.c busybox.3/procps/top.c
--- busybox.2/procps/top.c	2010-08-16 20:44:55.000000000 +0200
+++ busybox.3/procps/top.c	2010-08-24 08:24:11.000000000 +0200
@@ -942,20 +942,20 @@ int top_main(int argc UNUSED_PARAM, char
 			}
 #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" */
_______________________________________________
busybox mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to