aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Shishkin <virtuoso@slind.org>2010-08-28 23:20:34 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2010-08-28 23:20:34 +0200
commit0834a6d3b9daec1f460c3cc836136ace12c53df0 (patch)
tree5a1ff91bf23113224ee2a54334c020ff97e0f19a
parent74c992af5c6b8cfe6214e705bc04f8c2f9d8a304 (diff)
downloadbusybox-w32-0834a6d3b9daec1f460c3cc836136ace12c53df0.tar.gz
busybox-w32-0834a6d3b9daec1f460c3cc836136ace12c53df0.tar.bz2
busybox-w32-0834a6d3b9daec1f460c3cc836136ace12c53df0.zip
pmap: new applet. +1k.
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 <virtuoso@slind.org> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--include/libbb.h31
-rw-r--r--libbb/procps.c160
-rw-r--r--procps/pmap.c111
-rw-r--r--procps/top.c16
4 files changed, 251 insertions, 67 deletions
diff --git a/include/libbb.h b/include/libbb.h
index 3fd754511..43e525cb9 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1387,6 +1387,29 @@ enum { COMM_LEN = TASK_COMM_LEN };
1387enum { COMM_LEN = 16 }; 1387enum { COMM_LEN = 16 };
1388# endif 1388# endif
1389#endif 1389#endif
1390
1391struct smaprec {
1392 unsigned long mapped_rw;
1393 unsigned long mapped_ro;
1394 unsigned long shared_clean;
1395 unsigned long shared_dirty;
1396 unsigned long private_clean;
1397 unsigned long private_dirty;
1398 unsigned long stack;
1399 unsigned long smap_pss, smap_swap;
1400 unsigned long smap_size;
1401 unsigned long smap_start;
1402 char smap_mode[5];
1403 char *smap_name;
1404};
1405
1406#if !ENABLE_PMAP
1407#define procps_read_smaps(pid, total, cb, data) \
1408 procps_read_smaps(pid, total)
1409#endif
1410int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
1411 void (*cb)(struct smaprec *, void *), void *data);
1412
1390typedef struct procps_status_t { 1413typedef struct procps_status_t {
1391 DIR *dir; 1414 DIR *dir;
1392 IF_FEATURE_SHOW_THREADS(DIR *task_dir;) 1415 IF_FEATURE_SHOW_THREADS(DIR *task_dir;)
@@ -1415,13 +1438,7 @@ typedef struct procps_status_t {
1415#endif 1438#endif
1416 unsigned tty_major,tty_minor; 1439 unsigned tty_major,tty_minor;
1417#if ENABLE_FEATURE_TOPMEM 1440#if ENABLE_FEATURE_TOPMEM
1418 unsigned long mapped_rw; 1441 struct smaprec smaps;
1419 unsigned long mapped_ro;
1420 unsigned long shared_clean;
1421 unsigned long shared_dirty;
1422 unsigned long private_clean;
1423 unsigned long private_dirty;
1424 unsigned long stack;
1425#endif 1442#endif
1426 char state[4]; 1443 char state[4];
1427 /* basename of executable in exec(2), read from /proc/N/stat 1444 /* basename of executable in exec(2), read from /proc/N/stat
diff --git a/libbb/procps.c b/libbb/procps.c
index 16992b670..14d4481bd 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -137,12 +137,9 @@ static unsigned long fast_strtoul_16(char **endptr)
137 *endptr = str; /* We skip trailing space! */ 137 *endptr = str; /* We skip trailing space! */
138 return n; 138 return n;
139} 139}
140/* TOPMEM uses fast_strtoul_10, so... */
141# undef ENABLE_FEATURE_FAST_TOP
142# define ENABLE_FEATURE_FAST_TOP 1
143#endif 140#endif
144 141
145#if ENABLE_FEATURE_FAST_TOP 142#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
146/* We cut a lot of corners here for speed */ 143/* We cut a lot of corners here for speed */
147static unsigned long fast_strtoul_10(char **endptr) 144static unsigned long fast_strtoul_10(char **endptr)
148{ 145{
@@ -177,6 +174,111 @@ static char *skip_fields(char *str, int count)
177} 174}
178#endif 175#endif
179 176
177#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
178int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
179 void (*cb)(struct smaprec *, void *), void *data)
180{
181 FILE *file;
182 struct smaprec currec;
183 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
184 char buf[PROCPS_BUFSIZE];
185#if !ENABLE_PMAP
186 void (*cb)(struct smaprec *, void *) = NULL;
187 void *data = NULL;
188#endif
189
190 sprintf(filename, "/proc/%u/smaps", (int)pid);
191
192 file = fopen_for_read(filename);
193 if (!file)
194 return 1;
195
196 memset(&currec, 0, sizeof(currec));
197 while (fgets(buf, PROCPS_BUFSIZE, file)) {
198 // Each mapping datum has this form:
199 // f7d29000-f7d39000 rw-s ADR M:m OFS FILE
200 // Size: nnn kB
201 // Rss: nnn kB
202 // .....
203
204 char *tp = buf, *p;
205
206#define SCAN(S, X) \
207 if (strncmp(tp, S, sizeof(S)-1) == 0) { \
208 tp = skip_whitespace(tp + sizeof(S)-1); \
209 total->X += currec.X = fast_strtoul_10(&tp); \
210 continue; \
211 }
212 if (cb) {
213 SCAN("Pss:" , smap_pss );
214 SCAN("Swap:" , smap_swap );
215 }
216 SCAN("Private_Dirty:", private_dirty);
217 SCAN("Private_Clean:", private_clean);
218 SCAN("Shared_Dirty:" , shared_dirty );
219 SCAN("Shared_Clean:" , shared_clean );
220#undef SCAN
221 tp = strchr(buf, '-');
222 if (tp) {
223 // We reached next mapping - the line of this form:
224 // f7d29000-f7d39000 rw-s ADR M:m OFS FILE
225
226 if (cb) {
227 /* If we have a previous record, there's nothing more
228 * for it, call the callback and clear currec
229 */
230 if (currec.smap_size)
231 cb(&currec, data);
232 free(currec.smap_name);
233 }
234 memset(&currec, 0, sizeof(currec));
235
236 *tp = ' ';
237 tp = buf;
238 currec.smap_start = fast_strtoul_16(&tp);
239 currec.smap_size = (fast_strtoul_16(&tp) - currec.smap_start) >> 10;
240
241 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1);
242
243 // skipping "rw-s ADR M:m OFS "
244 tp = skip_whitespace(skip_fields(tp, 4));
245 // filter out /dev/something (something != zero)
246 if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) {
247 if (currec.smap_mode[1] == 'w') {
248 currec.mapped_rw = currec.smap_size;
249 total->mapped_rw += currec.smap_size;
250 } else if (currec.smap_mode[1] == '-') {
251 currec.mapped_ro = currec.smap_size;
252 total->mapped_ro += currec.smap_size;
253 }
254 }
255
256 if (strcmp(tp, "[stack]\n") == 0)
257 total->stack += currec.smap_size;
258 if (cb) {
259 p = skip_non_whitespace(tp);
260 if (p == tp) {
261 currec.smap_name = xstrdup(" [ anon ]");
262 } else {
263 *p = '\0';
264 currec.smap_name = xstrdup(tp);
265 }
266 }
267 total->smap_size += currec.smap_size;
268 }
269 }
270 fclose(file);
271
272 if (cb) {
273 if (currec.smap_size)
274 cb(&currec, data);
275 free(currec.smap_name);
276 }
277
278 return 0;
279}
280#endif
281
180void BUG_comm_size(void); 282void BUG_comm_size(void);
181procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) 283procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
182{ 284{
@@ -365,54 +467,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
365 } 467 }
366 468
367#if ENABLE_FEATURE_TOPMEM 469#if ENABLE_FEATURE_TOPMEM
368 if (flags & (PSSCAN_SMAPS)) { 470 if (flags & PSSCAN_SMAPS)
369 FILE *file; 471 procps_read_smaps(pid, &sp->smaps, NULL, NULL);
370
371 strcpy(filename_tail, "smaps");
372 file = fopen_for_read(filename);
373 if (file) {
374 while (fgets(buf, sizeof(buf), file)) {
375 unsigned long sz;
376 char *tp;
377 char w;
378#define SCAN(str, name) \
379 if (strncmp(buf, str, sizeof(str)-1) == 0) { \
380 tp = skip_whitespace(buf + sizeof(str)-1); \
381 sp->name += fast_strtoul_10(&tp); \
382 continue; \
383 }
384 SCAN("Shared_Clean:" , shared_clean );
385 SCAN("Shared_Dirty:" , shared_dirty );
386 SCAN("Private_Clean:", private_clean);
387 SCAN("Private_Dirty:", private_dirty);
388#undef SCAN
389 // f7d29000-f7d39000 rw-s ADR M:m OFS FILE
390 tp = strchr(buf, '-');
391 if (tp) {
392 *tp = ' ';
393 tp = buf;
394 sz = fast_strtoul_16(&tp); /* start */
395 sz = (fast_strtoul_16(&tp) - sz) >> 10; /* end - start */
396 // tp -> "rw-s" string
397 w = tp[1];
398 // skipping "rw-s ADR M:m OFS "
399 tp = skip_whitespace(skip_fields(tp, 4));
400 // filter out /dev/something (something != zero)
401 if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) {
402 if (w == 'w') {
403 sp->mapped_rw += sz;
404 } else if (w == '-') {
405 sp->mapped_ro += sz;
406 }
407 }
408//else printf("DROPPING %s (%s)\n", buf, tp);
409 if (strcmp(tp, "[stack]\n") == 0)
410 sp->stack += sz;
411 }
412 }
413 fclose(file);
414 }
415 }
416#endif /* TOPMEM */ 472#endif /* TOPMEM */
417#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS 473#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
418 if (flags & PSSCAN_RUIDGID) { 474 if (flags & PSSCAN_RUIDGID) {
diff --git a/procps/pmap.c b/procps/pmap.c
new file mode 100644
index 000000000..cfa94ed82
--- /dev/null
+++ b/procps/pmap.c
@@ -0,0 +1,111 @@
1/*
2 * pmap implementation for busybox
3 *
4 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
5 * Written by Alexander Shishkin <virtuoso@slind.org>
6 *
7 * Licensed under GPLv2 or later, see the LICENSE file in this source tree
8 * for details.
9 */
10
11//applet:IF_PMAP(APPLET(pmap, _BB_DIR_USR_BIN, _BB_SUID_DROP))
12//kbuild:lib-$(CONFIG_PMAP) += pmap.o
13
14//config:config PMAP
15//config: bool "pmap"
16//config: default y
17//config: help
18//config: Display processes' memory mappings.
19
20//usage:#define pmap_trivial_usage
21//usage: "[-x][-q] PID"
22//usage:#define pmap_full_usage "\n\n"
23//usage: "Display detailed precesses' memory usage\n"
24//usage: "\nOptions:"
25//usage: "\n -x show details"
26//usage: "\n -q quiet"
27
28#include "libbb.h"
29
30#if ULONG_MAX == 0xffffffff
31# define TABS "\t"
32# define AFMT "8"
33# define DASHES ""
34#else
35# define TABS "\t\t"
36# define AFMT "16"
37# define DASHES "--------"
38#endif
39
40enum {
41 OPT_x = 1 << 0,
42 OPT_q = 1 << 1,
43};
44
45static void print_smaprec(struct smaprec *currec, void *data)
46{
47 unsigned opt = (unsigned)data;
48
49 printf("%0" AFMT "lx ", currec->smap_start);
50
51 if (opt & OPT_x)
52 printf("%7lu %7lu %7lu %7lu ",
53 currec->smap_size,
54 currec->smap_pss,
55 currec->private_dirty,
56 currec->smap_swap);
57 else
58 printf("%7luK", currec->smap_size);
59
60 printf(" %.4s %s\n", currec->smap_mode, currec->smap_name);
61}
62
63static int procps_get_maps(pid_t pid, unsigned opt)
64{
65 struct smaprec total;
66 int ret;
67 char buf[256];
68
69 read_cmdline(buf, sizeof(buf), pid, "no such process");
70 printf("%u: %s\n", (int)pid, buf);
71
72 if (!(opt & OPT_q) && (opt & OPT_x))
73 puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping");
74
75 memset(&total, 0, sizeof(total));
76
77 ret = procps_read_smaps(pid, &total, print_smaprec, (void*)opt);
78 if (ret)
79 return ret;
80
81 if (!(opt & OPT_q)) {
82 if (opt & OPT_x)
83 printf("--------" DASHES " ------ ------ ------ ------\n"
84 "total" TABS " %7lu %7lu %7lu %7lu\n",
85 total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap);
86 else
87 printf("mapped: %luK\n", total.smap_size);
88 }
89
90 return 0;
91}
92
93int pmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
94int pmap_main(int argc UNUSED_PARAM, char **argv)
95{
96 unsigned opts;
97 int ret;
98
99 opts = getopt32(argv, "xq");
100 argv += optind;
101
102 ret = 0;
103 while (*argv) {
104 pid_t pid = xatoi_positive(*argv++);
105 /* GNU pmap returns 42 if any of the pids failed */
106 if (procps_get_maps(pid, opts) != 0)
107 ret = 42;
108 }
109
110 return ret;
111}
diff --git a/procps/top.c b/procps/top.c
index fd758099e..4f37878de 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -942,20 +942,20 @@ int top_main(int argc UNUSED_PARAM, char **argv)
942 } 942 }
943#if ENABLE_FEATURE_TOPMEM 943#if ENABLE_FEATURE_TOPMEM
944 else { /* TOPMEM */ 944 else { /* TOPMEM */
945 if (!(p->mapped_ro | p->mapped_rw)) 945 if (!(p->smaps.mapped_ro | p->smaps.mapped_rw))
946 continue; /* kernel threads are ignored */ 946 continue; /* kernel threads are ignored */
947 n = ntop; 947 n = ntop;
948 /* No bug here - top and topmem are the same */ 948 /* No bug here - top and topmem are the same */
949 top = xrealloc_vector(topmem, 6, ntop++); 949 top = xrealloc_vector(topmem, 6, ntop++);
950 strcpy(topmem[n].comm, p->comm); 950 strcpy(topmem[n].comm, p->comm);
951 topmem[n].pid = p->pid; 951 topmem[n].pid = p->pid;
952 topmem[n].vsz = p->mapped_rw + p->mapped_ro; 952 topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro;
953 topmem[n].vszrw = p->mapped_rw; 953 topmem[n].vszrw = p->smaps.mapped_rw;
954 topmem[n].rss_sh = p->shared_clean + p->shared_dirty; 954 topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty;
955 topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh; 955 topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh;
956 topmem[n].dirty = p->private_dirty + p->shared_dirty; 956 topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty;
957 topmem[n].dirty_sh = p->shared_dirty; 957 topmem[n].dirty_sh = p->smaps.shared_dirty;
958 topmem[n].stack = p->stack; 958 topmem[n].stack = p->smaps.stack;
959 } 959 }
960#endif 960#endif
961 } /* end of "while we read /proc" */ 961 } /* end of "while we read /proc" */