Two new options for pmap, -X and -XX

Both options provide more information about a process using -X and -XX
flags. The data comes from /proc/PID/smaps so it may vary.

Signed-off-by: Craig Small <csmall@enc.com.au>
This commit is contained in:
Dimitrios Apostolou 2012-09-27 22:08:04 +10:00 committed by Craig Small
parent 8e867659e6
commit faec340719
3 changed files with 209 additions and 12 deletions

9
pmap.1
View File

@ -5,7 +5,7 @@
.\" Licensed under version 2 of the GNU General Public License. .\" Licensed under version 2 of the GNU General Public License.
.\" Written by Albert Cahalan. .\" Written by Albert Cahalan.
.\" .\"
.TH PMAP "1" "June 2011" "procps-ng" "User Commands" .TH PMAP "1" "September 2012" "procps-ng" "User Commands"
.SH NAME .SH NAME
pmap \- report memory map of a process pmap \- report memory map of a process
.SH SYNOPSIS .SH SYNOPSIS
@ -32,6 +32,13 @@ and
address range. Notice that the low and high arguments are single string address range. Notice that the low and high arguments are single string
separated with comma. separated with comma.
.TP .TP
\fB\-X\fR
Show even more details than the \fB\-x\fR option. WARNING: format changes
according to \fI/proc/PID/smaps\fR
.TP
\fB\-XX\fR
Show everything the kernel provides
.TP
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
Display help text and exit. Display help text and exit.
.TP .TP

208
pmap.c
View File

@ -29,6 +29,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <assert.h>
#include "c.h" #include "c.h"
#include "fileutils.h" #include "fileutils.h"
@ -46,6 +47,9 @@ static void __attribute__ ((__noreturn__))
_(" %s [options] pid [pid ...]\n"), program_invocation_short_name); _(" %s [options] pid [pid ...]\n"), program_invocation_short_name);
fputs(USAGE_OPTIONS, out); fputs(USAGE_OPTIONS, out);
fputs(_(" -x, --extended show details\n" fputs(_(" -x, --extended show details\n"
" -X show even more details\n"
" WARNING: format changes according to /proc/PID/smaps\n"
" -XX show everything the kernel provides\n"
" -d, --device show the device format\n" " -d, --device show the device format\n"
" -q, --quiet do not display header and footer\n" " -q, --quiet do not display header and footer\n"
" -A, --range=<low>[,<high>] limit results to the given range\n"), out); " -A, --range=<low>[,<high>] limit results to the given range\n"), out);
@ -56,12 +60,16 @@ static void __attribute__ ((__noreturn__))
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
} }
static char mapbuf[1024];
static char cmdbuf[512];
static unsigned KLONG range_low; static unsigned KLONG range_low;
static unsigned KLONG range_high = ~0ull; static unsigned KLONG range_high = ~0ull;
static int d_option; static int d_option;
static int q_option; static int q_option;
static int x_option; static int x_option;
static int X_option;
static unsigned shm_minor = ~0u; static unsigned shm_minor = ~0u;
@ -159,11 +167,183 @@ static char *mapping_name(proc_t * p, unsigned KLONG addr,
return cp; return cp;
} }
#define DETAIL_LENGTH 32
#define DETL "31" /* for format strings */
#define NUM_LENGTH 21 /* python says: len(str(2**64)) == 20 */
#define NUML "20" /* for format strings */
struct listnode {
char description[DETAIL_LENGTH];
char value_str[NUM_LENGTH];
unsigned KLONG value;
unsigned KLONG total;
int max_width;
struct listnode *next;
};
static struct listnode *listhead=NULL, *listtail=NULL;
static int is_unimportant (char *s)
{
if (strcmp(s, "AnonHugePages") == 0) return 1;
if (strcmp(s, "KernelPageSize") == 0) return 1;
if (strcmp(s, "MMUPageSize") == 0) return 1;
if (strcmp(s, "Shared_Dirty") == 0) return 1;
if (strcmp(s, "Private_Dirty") == 0) return 1;
if (strcmp(s, "Shared_Clean") == 0) return 1;
if (strcmp(s, "Private_Clean") == 0) return 1;
return 0;
}
static void print_extended_maps (FILE *f)
{
struct listnode *listnode;
char flags[DETAIL_LENGTH], map_desc[128],
detail_desc[DETAIL_LENGTH], value_str[NUM_LENGTH],
start[NUM_LENGTH], end[NUM_LENGTH],
offset[NUM_LENGTH], inode[NUM_LENGTH],
dev[64], fmt_str[64];
int maxw1=0, maxw2=0, maxw3=0, maxw4=0, maxw5=0;
int nfields, firstmapping, footer_gap, i;
unsigned KLONG value;
char *ret;
char c;
ret = fgets(mapbuf, sizeof mapbuf, f);
firstmapping = 1;
while (ret != NULL) {
/* === READ MAPPING === */
map_desc[0] = '\0';
nfields = sscanf(mapbuf,
"%"NUML"[0-9a-f]-%"NUML"[0-9a-f] "
"%"DETL"s %"NUML"[0-9a-f] "
"%63[0-9a-f:] %"NUML"s %127[^\n]%c",
start, end, flags, offset,
dev, inode, map_desc, &c);
/* Must read at least up to inode, else something has changed! */
if (nfields < 6)
xerrx(EXIT_FAILURE, _("Unknown format in smaps file!"));
/* If line too long we dump everything else. */
while (c != '\n') {
ret = fgets(mapbuf, sizeof mapbuf, f);
c = mapbuf[strlen(mapbuf) - 1];
}
/* Store maximum widths for printing nice later */
if (strlen(start) > maxw1) maxw1 = strlen(start);
if (strlen(flags) > maxw2) maxw2 = strlen(flags);
if (strlen(offset) > maxw3) maxw3 = strlen(offset);
if (strlen(dev) > maxw4) maxw4 = strlen(dev);
if (strlen(inode) > maxw5) maxw5 = strlen(inode);
if (7 > maxw5) maxw5 = 7;
ret = fgets(mapbuf, sizeof mapbuf, f);
nfields = sscanf(mapbuf, "%"DETL"[^:]: %"NUML"[0-9] kB %c",
detail_desc, value_str, &c);
listnode = listhead;
/* === READ MAPPING DETAILS === */
while (ret != NULL && nfields == 2) {
if (X_option < 2 && is_unimportant(detail_desc))
goto loop_end;
/* === CREATE LIST AND FILL description FIELD === */
if (listnode == NULL) {
assert(firstmapping == 1);
listnode = calloc(1, sizeof *listnode);
if (listhead == NULL) {
assert(listtail == NULL);
listhead = listnode;
} else {
listtail->next = listnode;
}
listtail = listnode;
/* listnode was calloc()ed so all fields are already NULL! */
strcpy(listnode->description, detail_desc);
if (strlen(detail_desc) > 7)
listnode->max_width = strlen(detail_desc);
else
listnode->max_width = 7;
} else {
/* === LIST EXISTS === */
if ((listnode == NULL) ||
(strcmp(listnode->description, detail_desc) != 0))
xerrx(EXIT_FAILURE, "ERROR: %s %s",
_("inconsistent detail field in smaps file, line:\n"),
mapbuf);
}
strcpy(listnode->value_str, value_str);
sscanf(value_str, "%"KLF"u", &listnode->value);
listnode->total += listnode->value;
if (strlen(value_str) > listnode->max_width)
listnode->max_width = strlen(value_str);
listnode = listnode->next;
loop_end:
ret = fgets(mapbuf, sizeof mapbuf, f);
nfields = sscanf(mapbuf, "%"DETL"[^:]: %"NUML"[0-9] kB %c",
detail_desc, value_str, &c);
}
/* === PRINT THIS MAPPING === */
/* Print header */
if (firstmapping && !q_option) {
if (strlen("Address") > maxw1) maxw1 = strlen("Address");
if (strlen("Flags") > maxw2) maxw2 = strlen("Flags");
if (strlen("Offset") > maxw3) maxw3 = strlen("Offset");
if (strlen("Device") > maxw4) maxw4 = strlen("Device");
if (strlen("Inode") > maxw5) maxw5 = strlen("Inode");
sprintf(fmt_str, "%%%ds %%%ds %%%ds %%%ds %%%ds",
maxw1, maxw2, maxw3, maxw4, maxw5);
printf(fmt_str, "Address", "Flags", "Offset", "Device", "Inode");
for (listnode=listhead; listnode=listnode->next;
listnode!=NULL) {
sprintf(fmt_str, " %%%ds", listnode->max_width);
printf(fmt_str, listnode->description);
}
printf(" %s\n", "Description");
}
/* Print data */
sprintf(fmt_str, "%%%ds %%%ds %%%ds %%%ds %%%ds",
maxw1, maxw2, maxw3, maxw4, maxw5);
printf(fmt_str, start, flags, offset, dev, inode);
for (listnode=listhead; listnode=listnode->next;
listnode!=NULL) {
sprintf(fmt_str, " %%%ds", listnode->max_width);
printf(fmt_str, listnode->value_str);
}
printf(" %s\n", map_desc);
firstmapping = 0;
}
/* === PRINT TOTALS === */
if (!q_option) {
footer_gap = maxw1+maxw2+maxw3+maxw4+maxw5+5;
for (i=0; i<footer_gap; i++)
putc(' ', stdout);
for (listnode=listhead; listnode=listnode->next;
listnode!=NULL) {
for (i=0; i<listnode->max_width; i++)
putc('=', stdout);
putc(' ', stdout);
}
putc('\n', stdout);
for (i=0; i<footer_gap; i++)
putc(' ', stdout);
for (listnode=listhead; listnode=listnode->next;
listnode!=NULL) {
sprintf(fmt_str, "%%%dd ", listnode->max_width);
printf(fmt_str, listnode->total);
}
fputs("KB \n", stdout);
}
/* We don't free() the list, it's used for all PIDs passed as arguments */
}
static int one_proc(proc_t * p) static int one_proc(proc_t * p)
{ {
char buf[32]; char buf[32];
char mapbuf[9600];
char cmdbuf[512];
FILE *fp; FILE *fp;
unsigned long total_shared = 0ul; unsigned long total_shared = 0ul;
unsigned long total_private_readonly = 0ul; unsigned long total_private_readonly = 0ul;
@ -183,12 +363,13 @@ static int one_proc(proc_t * p)
*/ */
int maxcmd = 0xfffff; int maxcmd = 0xfffff;
sprintf(buf, "/proc/%u/maps", p->tgid); if (x_option || X_option) {
sprintf(buf, "/proc/%u/smaps", p->tgid);
if ((fp = fopen(buf, "r")) == NULL) if ((fp = fopen(buf, "r")) == NULL)
return 1; return 1;
if (x_option) { } else {
sprintf(buf, "/proc/%u/smaps", p->tgid); sprintf(buf, "/proc/%u/maps", p->tgid);
if ((fp = freopen(buf, "r", fp)) == NULL) if ((fp = fopen(buf, "r")) == NULL)
return 1; return 1;
} }
@ -196,6 +377,11 @@ static int one_proc(proc_t * p)
ESC_ARGS | ESC_BRACKETS); ESC_ARGS | ESC_BRACKETS);
printf("%u: %s\n", p->tgid, cmdbuf); printf("%u: %s\n", p->tgid, cmdbuf);
if (X_option) {
print_extended_maps(fp);
return 0;
}
if (!q_option && (x_option | d_option)) { if (!q_option && (x_option | d_option)) {
if (x_option) { if (x_option) {
if (sizeof(KLONG) == 4) if (sizeof(KLONG) == 4)
@ -431,11 +617,14 @@ int main(int argc, char **argv)
x_option = d_option = q_option = 0; x_option = d_option = q_option = 0;
while ((c = getopt_long(argc, argv, "xrdqA:hV", longopts, NULL)) != -1) while ((c = getopt_long(argc, argv, "xXrdqA:hV", longopts, NULL)) != -1)
switch (c) { switch (c) {
case 'x': case 'x':
x_option = 1; x_option = 1;
break; break;
case 'X':
X_option++;
break;
case 'r': case 'r':
xwarnx(_("option -r is ignored as SunOS compatibility")); xwarnx(_("option -r is ignored as SunOS compatibility"));
break; break;
@ -468,8 +657,9 @@ int main(int argc, char **argv)
if (argc < 1) if (argc < 1)
xerrx(EXIT_FAILURE, _("argument missing")); xerrx(EXIT_FAILURE, _("argument missing"));
if (d_option && x_option) if (d_option && (x_option || X_option) ||
xerrx(EXIT_FAILURE, _("options -d and -x cannot coexist")); x_option && (d_option || X_option))
xerrx(EXIT_FAILURE, _("options -d, -x, -X are mutually exclusive"));
pidlist = xmalloc(sizeof(unsigned) * argc); pidlist = xmalloc(sizeof(unsigned) * argc);

View File

@ -33,7 +33,7 @@
// //
#if defined(k64test) || (defined(_ABIN32) && _MIPS_SIM == _ABIN32) #if defined(k64test) || (defined(_ABIN32) && _MIPS_SIM == _ABIN32)
#define KLONG long long // not typedef; want "unsigned KLONG" to work #define KLONG long long // not typedef; want "unsigned KLONG" to work
#define KLF "L" #define KLF "ll"
#define STRTOUKL strtoull #define STRTOUKL strtoull
#else #else
#define KLONG long #define KLONG long