summaryrefslogtreecommitdiffstats
path: root/src/pmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmap.c')
-rw-r--r--src/pmap.c1217
1 files changed, 1217 insertions, 0 deletions
diff --git a/src/pmap.c b/src/pmap.c
new file mode 100644
index 0000000..ae57fd2
--- /dev/null
+++ b/src/pmap.c
@@ -0,0 +1,1217 @@
+/*
+ * pmap.c - print process memory mapping
+ *
+ * Copyright © 2010-2023 Craig Small <csmall@dropbear.xyz>
+ * Copyright © 2011-2023 Jim Warner <james.warner@comcast.net>
+ * Copyright © 2011-2012 Sami Kerola <kerolasa@iki.fi>
+ * Copyright © 2002-2009 Albert Cahalan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "c.h"
+#include "fileutils.h"
+#include "nls.h"
+#include "xalloc.h"
+
+#include "pids.h"
+
+static struct pids_info *Pids_info;
+
+enum pids_item Pid_items[] = {
+ PIDS_ID_PID, PIDS_ID_TGID,
+ PIDS_CMDLINE, PIDS_ADDR_STACK_START };
+enum rel_items { pid, tgid, cmdline, start_stack };
+
+const char *nls_Address,
+ *nls_Offset,
+ *nls_Device,
+ *nls_Mapping,
+ *nls_Perm,
+ *nls_Inode,
+ *nls_Kbytes,
+ *nls_Mode,
+ *nls_RSS,
+ *nls_Dirty;
+
+static void nls_initialize(void)
+{
+ setlocale (LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ /* these are the headings shared across all options,
+ though their widths when output might differ */
+ nls_Address = _("Address");
+ nls_Offset = _("Offset");
+ nls_Device = _("Device");
+ nls_Mapping = _("Mapping");
+
+ /* these headings are used only by the -X/-XX options,
+ and are not derived literally from /proc/#/smaps */
+ nls_Perm = _("Perm");
+ nls_Inode = _("Inode");
+
+ /* these are potentially used for options other than -X/-XX, */
+ nls_Kbytes = _("Kbytes");
+ nls_Mode = _("Mode");
+ nls_RSS = _("RSS");
+ nls_Dirty = _("Dirty");
+}
+
+static int justify_print(const char *str, int width, int right)
+{
+ if (width < 1)
+ puts(str);
+ else {
+ int len = strlen(str);
+ if (width < len) width = len;
+ printf(right ? "%*.*s " : "%-*.*s ", width, width, str);
+ }
+ return width;
+}
+
+static int integer_width(unsigned long number)
+{
+ int result;
+
+ result = !(number > 0);
+ while (number) {
+ result++;
+ number /= 10;
+ }
+
+ return result;
+}
+
+
+static void __attribute__ ((__noreturn__))
+usage(FILE * out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %s [options] PID [PID ...]\n"), program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -x, --extended show details\n"), out);
+ fputs(_(" -X show even more details\n"), out);
+ fputs(_(" WARNING: format changes according to /proc/PID/smaps\n"), out);
+ fputs(_(" -XX show everything the kernel provides\n"), out);
+ fputs(_(" -c, --read-rc read the default rc\n"), out);
+ fputs(_(" -C, --read-rc-from=<file> read the rc from file\n"), out);
+ fputs(_(" -n, --create-rc create new default rc\n"), out);
+ fputs(_(" -N, --create-rc-to=<file> create new rc to file\n"), out);
+ fputs(_(" NOTE: pid arguments are not allowed with -n, -N\n"), out);
+ fputs(_(" -d, --device show the device format\n"), out);
+ fputs(_(" -q, --quiet do not display header and footer\n"), out);
+ fputs(_(" -p, --show-path show path in the mapping\n"), out);
+ fputs(_(" -A, --range=<low>[,<high>] limit results to the given range\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("pmap(1)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static char mapbuf[1024];
+
+static unsigned long range_low;
+static unsigned long range_high = ~0ul;
+
+static int c_option = 0;
+static int C_option = 0;
+static int d_option = 0;
+static int n_option = 0;
+static int N_option = 0;
+static int q_option = 0;
+static int x_option = 0;
+static int X_option = 0;
+
+static int map_desc_showpath;
+
+static unsigned shm_minor = ~0u;
+
+static void discover_shm_minor(void)
+{
+ void *addr;
+ int shmid;
+ char mapbuf_b[256];
+ FILE *fp;
+
+ if ( (fp = fopen("/proc/self/maps", "r")) == NULL)
+ return;
+
+ /* create */
+ shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666);
+ if (shmid == -1)
+ {
+ /* failed; oh well */
+ fclose(fp);
+ return;
+ }
+ /* attach */
+ addr = shmat(shmid, NULL, SHM_RDONLY);
+ if (addr == (void *)-1)
+ goto out_destroy;
+
+ while (fgets(mapbuf_b, sizeof mapbuf_b, fp)) {
+ char perms[32];
+ /* to clean up unprintables */
+ char *tmp;
+ unsigned long start, end;
+ unsigned long long file_offset, inode;
+ unsigned dev_major, dev_minor;
+ if (sscanf(mapbuf_b, "%lx-%lx %31s %llx %x:%x %llu", &start,
+ &end, perms, &file_offset, &dev_major, &dev_minor, &inode) < 6)
+ continue;
+ tmp = strchr(mapbuf_b, '\n');
+ if (tmp)
+ *tmp = '\0';
+ tmp = mapbuf_b;
+ while (*tmp) {
+ if (!isprint(*tmp))
+ *tmp = '?';
+ tmp++;
+ }
+ if (start > (unsigned long)addr)
+ continue;
+ if (dev_major)
+ continue;
+ if (perms[3] != 's')
+ continue;
+ if (strstr(mapbuf_b, "/SYSV")) {
+ shm_minor = dev_minor;
+ break;
+ }
+ }
+
+ if (shmdt(addr))
+ perror(_("shared memory detach"));
+
+out_destroy:
+ fclose(fp);
+ if (shmctl(shmid, IPC_RMID, NULL) && errno != EINVAL)
+ perror(_("shared memory remove"));
+
+ return;
+}
+
+static const char *mapping_name(struct pids_stack *p, unsigned long addr,
+ unsigned long len, const char *mapbuf_b,
+ unsigned showpath, unsigned dev_major,
+ unsigned dev_minor, unsigned long long inode)
+{
+ const char *cp;
+
+ if (!dev_major && dev_minor == shm_minor && strstr(mapbuf_b, "/SYSV")) {
+ static char shmbuf[64];
+ snprintf(shmbuf, sizeof shmbuf, " [ shmid=0x%llx ]", inode);
+ return shmbuf;
+ }
+
+ cp = strrchr(mapbuf_b, '/');
+ if (cp) {
+ if (showpath)
+ return strchr(mapbuf_b, '/');
+ return cp[1] ? cp + 1 : cp;
+ }
+
+ cp = _(" [ anon ]");
+ if (PIDS_VAL(start_stack, ul_int, p, Pids_info) >= addr
+ && (PIDS_VAL(start_stack, ul_int, p, Pids_info) <= addr + len))
+ cp = _(" [ stack ]");
+ 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 */
+#define VMFLAGS_LENGTH 128 /* 30 2-char space-separated flags == 90+1, but be safe */
+#define VMFL "127" /* for format strings */
+
+struct listnode {
+ char description[DETAIL_LENGTH];
+ char value_str[NUM_LENGTH];
+ unsigned long value;
+ unsigned long total;
+ int max_width;
+ struct listnode *next;
+};
+
+static struct listnode *listhead=NULL, *listtail=NULL, *listnode;
+
+
+struct cnf_listnode {
+ char description[DETAIL_LENGTH];
+ struct cnf_listnode *next;
+};
+
+static struct cnf_listnode *cnf_listhead=NULL, *cnf_listnode;
+
+static int is_unimportant (const 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;
+ if (strcmp(s, "VmFlags") == 0) return 1;
+ return 0;
+}
+
+/* check, whether we want to display the field or not */
+static int is_enabled (const char *s)
+{
+ if (X_option == 1) return !is_unimportant(s);
+
+ if (c_option) { /* taking the list of disabled fields from the rc file */
+
+ for (cnf_listnode = cnf_listhead; cnf_listnode; cnf_listnode = cnf_listnode -> next) {
+ if (!strcmp(s, cnf_listnode -> description)) return 1;
+ }
+ return 0;
+
+ }
+
+ return 1;
+}
+
+
+static void print_extended_maps (FILE *f)
+{
+ char perms[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], vmflags[VMFLAGS_LENGTH];
+ int maxw1=0, maxw2=0, maxw3=0, maxw4=0, maxw5=0, maxwv=0;
+ int nfields, firstmapping, footer_gap, i, maxw_;
+ char *ret, *map_basename, c, has_vmflags = 0;
+
+ ret = fgets(mapbuf, sizeof mapbuf, f);
+ firstmapping = 2;
+ 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]",
+ start, end, perms, offset,
+ dev, inode, map_desc);
+ /* 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. */
+ c = mapbuf[strlen(mapbuf) - 1];
+ while (c != '\n') {
+ fgets(mapbuf, sizeof mapbuf, f);
+ if (!ret || !mapbuf[0])
+ xerrx(EXIT_FAILURE, _("Unknown format in smaps file!"));
+ c = mapbuf[strlen(mapbuf) - 1];
+ }
+
+ /* Store maximum widths for printing nice later */
+ if (strlen(start ) > maxw1) maxw1 = strlen(start);
+ if (strlen(perms ) > maxw2) maxw2 = strlen(perms);
+ if (strlen(offset) > maxw3) maxw3 = strlen(offset);
+ if (strlen(dev ) > maxw4) maxw4 = strlen(dev);
+ if (strlen(inode ) > maxw5) maxw5 = strlen(inode);
+
+ ret = fgets(mapbuf, sizeof mapbuf, f);
+ nfields = ret ? sscanf(mapbuf, "%"DETL"[^:]: %"NUML"[0-9] kB %c",
+ detail_desc, value_str, &c) : 0;
+ listnode = listhead;
+ /* === READ MAPPING DETAILS === */
+ while (ret != NULL && nfields == 2) {
+
+ if (!is_enabled(detail_desc)) goto loop_end;
+
+ /* === CREATE LIST AND FILL description FIELD === */
+ if (listnode == NULL) {
+ assert(firstmapping == 2);
+ listnode = calloc(1, sizeof *listnode);
+ if (listnode == NULL)
+ xerrx(EXIT_FAILURE, _("ERROR: memory allocation failed"));
+
+ 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 (!q_option) listnode->max_width = strlen(detail_desc);
+ else listnode->max_width = 0;
+ } else {
+ /* === LIST EXISTS === */
+ if (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, "%lu", &listnode->value);
+ if (firstmapping == 2) {
+ listnode->total += listnode->value;
+ if (q_option) {
+ maxw_ = strlen(listnode->value_str);
+ if (maxw_ > listnode->max_width)
+ listnode->max_width = maxw_;
+ }
+ }
+ listnode = listnode->next;
+loop_end:
+ ret = fgets(mapbuf, sizeof mapbuf, f);
+ nfields = ret ? sscanf(mapbuf, "%"DETL"[^:]: %"NUML"[0-9] kB %c",
+ detail_desc, value_str, &c) : 0;
+ }
+
+ /* === GET VMFLAGS === */
+ nfields = ret ? sscanf(mapbuf, "VmFlags: %"VMFL"[a-z ]", vmflags) : 0;
+ if (nfields == 1) {
+ int len = strlen(vmflags);
+ if (len > 0 && vmflags[len-1] == ' ') vmflags[--len] = '\0';
+ if (len > maxwv) maxwv = len;
+ if (! has_vmflags) has_vmflags = 1;
+ ret = fgets(mapbuf, sizeof mapbuf, f);
+ }
+
+ if (firstmapping == 2) { /* width measurement stage, do not print anything yet */
+ if (ret == NULL) { /* once the end of file is reached ...*/
+ firstmapping = 1; /* ... we reset the file position to the beginning of the file */
+ fseek(f, 0, SEEK_SET); /* ... and repeat the process with printing enabled */
+ ret = fgets(mapbuf, sizeof mapbuf, f); /* this is not ideal and needs to be redesigned one day */
+
+ if (!q_option) {
+ /* calculate width of totals */
+ for (listnode=listhead; listnode!=NULL; listnode=listnode->next) {
+ maxw_ = integer_width(listnode->total);
+ if (maxw_ > listnode->max_width)
+ listnode->max_width = maxw_;
+ }
+ }
+
+ }
+ } else { /* the maximum widths have been measured, we've already reached the printing stage */
+ /* === PRINT THIS MAPPING === */
+
+ /* Print header */
+ if (firstmapping && !q_option) {
+
+ maxw1 = justify_print(nls_Address, maxw1, 1);
+
+ if (is_enabled(nls_Perm))
+ maxw2 = justify_print(nls_Perm, maxw2, 1);
+
+ if (is_enabled(nls_Offset))
+ maxw3 = justify_print(nls_Offset, maxw3, 1);
+
+ if (is_enabled(nls_Device))
+ maxw4 = justify_print(nls_Device, maxw4, 1);
+
+ if (is_enabled(nls_Inode))
+ maxw5 = justify_print(nls_Inode, maxw5, 1);
+
+ for (listnode=listhead; listnode!=NULL; listnode=listnode->next)
+ justify_print(listnode->description, listnode->max_width, 1);
+
+ if (has_vmflags && is_enabled("VmFlags"))
+ maxwv = justify_print("VmFlags", maxwv, 1);
+
+ if (is_enabled(nls_Mapping))
+ justify_print(nls_Mapping, 0, 0);
+ else
+ printf("\n");
+ }
+
+ /* Print data */
+ printf("%*s", maxw1, start); /* Address field is always enabled */
+
+ if (is_enabled(nls_Perm))
+ printf(" %*s", maxw2, perms);
+
+ if (is_enabled(nls_Offset))
+ printf(" %*s", maxw3, offset);
+
+ if (is_enabled(nls_Device))
+ printf(" %*s", maxw4, dev);
+
+ if (is_enabled(nls_Inode))
+ printf(" %*s", maxw5, inode);
+
+ for (listnode=listhead; listnode!=NULL; listnode=listnode->next)
+ printf(" %*s", listnode->max_width, listnode->value_str);
+
+ if (has_vmflags && is_enabled("VmFlags"))
+ printf(" %*s", maxwv, vmflags);
+
+ if (is_enabled(nls_Mapping)) {
+ if (map_desc_showpath) {
+ printf(" %s", map_desc);
+ } else {
+ map_basename = strrchr(map_desc, '/');
+ if (!map_basename) {
+ printf(" %s", map_desc);
+ } else {
+ printf(" %s", map_basename + 1);
+ }
+
+ }
+ }
+
+ printf("\n");
+
+ firstmapping = 0;
+
+ }
+ }
+ /* === PRINT TOTALS === */
+ if (!q_option && listhead!=NULL) { /* footer enabled and non-empty */
+
+ footer_gap = maxw1 + 1; /* Address field is always enabled */
+ if (is_enabled(nls_Perm )) footer_gap += maxw2 + 1;
+ if (is_enabled(nls_Offset)) footer_gap += maxw3 + 1;
+ if (is_enabled(nls_Device)) footer_gap += maxw4 + 1;
+ if (is_enabled(nls_Inode )) footer_gap += maxw5 + 1;
+
+ for (i=0; i<footer_gap; i++) putc(' ', stdout);
+
+ for (listnode=listhead; listnode!=NULL; listnode=listnode->next) {
+ 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!=NULL; listnode=listnode->next) {
+ printf("%*lu ", listnode->max_width, listnode->total);
+ listnode->total = 0;
+ }
+
+ fputs("KB \n", stdout);
+ }
+ /* We don't free() the list, it's used for all PIDs passed as arguments */
+}
+
+ // variable placed here to silence compiler 'uninitialized' warning
+static unsigned long start_To_Avoid_Warning;
+
+static int one_proc (struct pids_stack *p)
+{
+ char buf[32];
+ FILE *fp;
+ unsigned long total_shared = 0ul;
+ unsigned long total_private_readonly = 0ul;
+ unsigned long total_private_writeable = 0ul;
+ unsigned long diff = 0;
+ unsigned long end;
+ char perms[32] = "";
+ const char *cp2 = NULL;
+ unsigned long long rss = 0ull;
+ unsigned long long private_dirty = 0ull;
+ unsigned long long shared_dirty = 0ull;
+ unsigned long long total_rss = 0ull;
+ unsigned long long total_private_dirty = 0ull;
+ unsigned long long total_shared_dirty = 0ull;
+ int maxw1=0, maxw2=0, maxw3=0, maxw4=0, maxw5=0;
+
+ printf("%u: %s\n", PIDS_VAL(tgid, s_int, p, Pids_info), PIDS_VAL(cmdline, str, p, Pids_info));
+
+ if (x_option || X_option || c_option) {
+ snprintf(buf, sizeof buf, "/proc/%u/smaps", PIDS_VAL(tgid, s_int, p, Pids_info));
+ if ((fp = fopen(buf, "r")) == NULL)
+ return 1;
+ } else {
+ snprintf(buf, sizeof buf, "/proc/%u/maps", PIDS_VAL(tgid, s_int, p, Pids_info));
+ if ((fp = fopen(buf, "r")) == NULL)
+ return 1;
+ }
+
+ if (X_option || c_option) {
+ print_extended_maps(fp);
+ fclose(fp);
+ return 0;
+ }
+
+ if (x_option) {
+ maxw1 = 16;
+ if (sizeof(long) == 4) maxw1 = 8;
+ maxw2 = maxw3 = maxw4 = 7;
+ maxw5 = 5;
+ if (!q_option) {
+ maxw1 = justify_print(nls_Address, maxw1, 0);
+ maxw2 = justify_print(nls_Kbytes, maxw2, 1);
+ maxw3 = justify_print(nls_RSS, maxw3, 1);
+ maxw4 = justify_print(nls_Dirty, maxw4, 1);
+ maxw5 = justify_print(nls_Mode, maxw5, 0);
+ justify_print(nls_Mapping, 0, 0);
+ }
+ }
+
+ if (d_option) {
+ maxw1 = 16;
+ if (sizeof(long) == 4) maxw1 = 8;
+ maxw2 = 7;
+ maxw3 = 5;
+ maxw4 = 16;
+ maxw5 = 9;
+ if (!q_option) {
+ maxw1 = justify_print(nls_Address, maxw1, 0);
+ maxw2 = justify_print(nls_Kbytes, maxw2, 1);
+ maxw3 = justify_print(nls_Mode, maxw3, 0);
+ maxw4 = justify_print(nls_Offset, maxw4, 0);
+ maxw5 = justify_print(nls_Device, maxw5, 0);
+ justify_print(nls_Mapping, 0, 0);
+ }
+ }
+
+ while (fgets(mapbuf, sizeof mapbuf, fp)) {
+ /* to clean up unprintables */
+ char *tmp;
+ unsigned long long file_offset, inode;
+ unsigned dev_major, dev_minor;
+ unsigned long long smap_value;
+ char smap_key[21];
+
+ /* hex values are lower case or numeric, keys are upper */
+ if (mapbuf[0] >= 'A' && mapbuf[0] <= 'Z') {
+ /* Its a key */
+ if (sscanf(mapbuf, "%20[^:]: %llu", smap_key, &smap_value) == 2) {
+ if (strcmp("Rss", smap_key) == 0) {
+ rss = smap_value;
+ total_rss += smap_value;
+ continue;
+ }
+ if (strcmp("Shared_Dirty", smap_key) == 0) {
+ shared_dirty = smap_value;
+ total_shared_dirty += smap_value;
+ continue;
+ }
+ if (strcmp("Private_Dirty", smap_key) == 0) {
+ private_dirty = smap_value;
+ total_private_dirty += smap_value;
+ continue;
+ }
+ if (strcmp("Swap", smap_key) == 0) {
+ /* doesn't matter as long as last */
+ if (cp2)
+ printf("%0*lx %*lu %*llu %*llu %*s %s\n",
+ maxw1, start_To_Avoid_Warning,
+ maxw2, (unsigned long)(diff >> 10),
+ maxw3, rss,
+ maxw4, (private_dirty + shared_dirty),
+ maxw5, perms,
+ cp2);
+ /* reset some counters */
+ rss = shared_dirty = private_dirty = 0ull;
+ diff = end = 0;
+ perms[0] = '\0';
+ cp2 = NULL;
+ continue;
+ }
+ }
+ /* Other keys or not a key-value pair */
+ continue;
+ }
+ sscanf(mapbuf, "%lx-%lx %31s %llx %x:%x %llu",
+ &start_To_Avoid_Warning,
+ &end, perms, &file_offset, &dev_major, &dev_minor,
+ &inode);
+
+ if (end - 1 < range_low)
+ continue;
+ if (range_high < start_To_Avoid_Warning)
+ break;
+
+ tmp = strchr(mapbuf, '\n');
+ if (tmp)
+ *tmp = '\0';
+ tmp = mapbuf;
+ while (*tmp) {
+ if (!isprint(*tmp))
+ *tmp = '?';
+ tmp++;
+ }
+
+ diff = end - start_To_Avoid_Warning;
+ if (perms[3] == 's')
+ total_shared += diff;
+ if (perms[3] == 'p') {
+ perms[3] = '-';
+ if (perms[1] == 'w')
+ total_private_writeable += diff;
+ else
+ total_private_readonly += diff;
+ }
+ /* format used by Solaris 9 and procps-3.2.0+ an 'R'
+ * if swap not reserved (MAP_NORESERVE, SysV ISM
+ * shared mem, etc.)
+ */
+ perms[4] = '-';
+ perms[5] = '\0';
+
+ if (x_option) {
+ cp2 =
+ mapping_name(p, start_To_Avoid_Warning, diff, mapbuf, map_desc_showpath, dev_major,
+ dev_minor, inode);
+ /* printed with the keys */
+ continue;
+ }
+ if (d_option) {
+ const char *cp =
+ mapping_name(p, start_To_Avoid_Warning, diff, mapbuf, map_desc_showpath, dev_major,
+ dev_minor, inode);
+ printf("%0*lx %*lu %*s %0*llx %*.*s%03x:%05x %s\n",
+ maxw1, start_To_Avoid_Warning,
+ maxw2, (unsigned long)(diff >> 10),
+ maxw3, perms,
+ maxw4, file_offset,
+ (maxw5-9), (maxw5-9), " ", dev_major, dev_minor,
+ cp);
+ }
+ if (!x_option && !d_option) {
+ const char *cp =
+ mapping_name(p, start_To_Avoid_Warning, diff, mapbuf, map_desc_showpath, dev_major,
+ dev_minor, inode);
+ printf((sizeof(long) == 8)
+ ? "%016lx %6luK %s %s\n"
+ : "%08lx %6luK %s %s\n",
+ start_To_Avoid_Warning, (unsigned long)(diff >> 10), perms, cp);
+ }
+
+ }
+ fclose(fp);
+ if (!q_option) {
+ if (x_option) {
+ if (sizeof(long) == 4)
+ justify_print("--------", maxw1, 0);
+ else
+ justify_print("----------------", maxw1, 0);
+ justify_print("-------", maxw2, 1);
+ justify_print("-------", maxw3, 1);
+ justify_print("-------", maxw4, 1);
+ printf("\n");
+
+ printf("%-*s ", maxw1, _("total kB"));
+ printf("%*ld %*llu %*llu\n",
+ maxw2, (total_shared +
+ total_private_writeable +
+ total_private_readonly) >> 10,
+ maxw3, total_rss,
+ maxw4, (total_shared_dirty +
+ total_private_dirty));
+ }
+ if (d_option) {
+ printf
+ (_("mapped: %ldK writeable/private: %ldK shared: %ldK\n"),
+ (total_shared + total_private_writeable +
+ total_private_readonly) >> 10,
+ total_private_writeable >> 10, total_shared >> 10);
+ }
+ if (!x_option && !d_option) {
+ if (sizeof(long) == 8)
+ /* Translation Hint: keep total string length
+ * as 24 characters. Adjust %16 if needed*/
+ printf(_(" total %16ldK\n"),
+ (total_shared + total_private_writeable +
+ total_private_readonly) >> 10);
+ else
+ /* Translation Hint: keep total string length
+ * as 16 characters. Adjust %8 if needed*/
+ printf(_(" total %8ldK\n"),
+ (total_shared + total_private_writeable +
+ total_private_readonly) >> 10);
+ }
+ }
+
+ return 0;
+}
+
+static void range_arguments(const char *optarg)
+{
+ char *buf, *arg1, *arg2;
+
+ if ((buf = xstrdup(optarg)) == NULL) {
+ xerrx(EXIT_FAILURE, "%s: '%s'", _("failed to parse argument"),
+ (optarg?optarg:"(null)"));
+ }
+ arg1 = buf;
+ arg2 = strchr(arg1, ',');
+ if (arg2)
+ *arg2++ = '\0';
+ else
+ arg2 = arg1;
+ if (arg1[0] != '\0')
+ range_low = strtoul(arg1, &arg1, 16);
+ if (arg2[0] != '\0')
+ range_high = strtoul(arg2, &arg2, 16);
+ if (*arg1 || *arg2) {
+ free(buf);
+ xerrx(EXIT_FAILURE, "%s: '%s'", _("failed to parse argument"),
+ optarg);
+ }
+ free(buf);
+}
+
+
+#define MAX_CNF_LINE_LEN 1024
+
+#define SECTION_ID_NONE 0
+#define SECTION_ID_UNSUPPORTED 1
+
+#define SECTION_STR_FIELDS_DISPLAY "[Fields Display]"
+#define SECTION_STR_FIELDS_DISPLAY_LEN (sizeof(SECTION_STR_FIELDS_DISPLAY) - 1)
+#define SECTION_ID_FIELDS_DISPLAY 2
+
+#define SECTION_STR_MAPPING "[Mapping]"
+#define SECTION_STR_MAPPING_LEN (sizeof(SECTION_STR_MAPPING) - 1)
+#define SECTION_ID_MAPPING 3
+
+static int config_read (char *rc_filename)
+{
+ FILE *f;
+ char line_buf[MAX_CNF_LINE_LEN + 1];
+ char tmp_buf [MAX_CNF_LINE_LEN + 1];
+ char *trimmed;
+ int length;
+ char *tail, *token;
+ int line_cnt, section_id;
+
+ f = fopen(rc_filename, "r");
+
+ if (!f) return 0; /* can't open the file for reading */
+
+ line_cnt = 0;
+ section_id = SECTION_ID_NONE;
+
+ while (fgets (line_buf, MAX_CNF_LINE_LEN + 1, f)) {
+
+ line_cnt++;
+
+ /* get rid of the LF char */
+ length = strlen(line_buf);
+ if (length > 0 && line_buf[length - 1] == '\n') {
+ line_buf[length - 1] = '\0';
+ } else if (length == MAX_CNF_LINE_LEN) { /* no LF char -> line too long */
+ xwarnx(_("config line too long - line %d"), line_cnt);
+ /* ignoring the tail */
+ while (fgets (tmp_buf, MAX_CNF_LINE_LEN + 1, f) &&
+ (length = strlen(tmp_buf))>0 &&
+ tmp_buf[length - 1] != '\n') ;
+ }
+
+ /* trim leading whitespaces */
+ trimmed = line_buf;
+ while (*trimmed == ' ' || *trimmed == '\t') trimmed++;
+
+ /* skip comments and empty lines */
+ if (*trimmed == '#' || *trimmed == '\0') continue;
+
+ if (*trimmed == '[') { /* section */
+ if (!strncmp(trimmed, SECTION_STR_FIELDS_DISPLAY, SECTION_STR_FIELDS_DISPLAY_LEN)) {
+ trimmed += SECTION_STR_FIELDS_DISPLAY_LEN;
+ section_id = SECTION_ID_FIELDS_DISPLAY;
+ } else if (!strncmp(trimmed, SECTION_STR_MAPPING, SECTION_STR_MAPPING_LEN)) {
+ trimmed += SECTION_STR_MAPPING_LEN;
+ section_id = SECTION_ID_MAPPING;
+ } else {
+ while (*trimmed != ']' && *trimmed != '\0') trimmed++;
+ if (*trimmed == ']') {
+ section_id = SECTION_ID_UNSUPPORTED;
+ xwarnx(_("unsupported section found in the config - line %d"), line_cnt);
+ trimmed++;
+ } else {
+ xwarnx(_("syntax error found in the config - line %d"), line_cnt);
+ }
+ }
+
+ /* trim trailing whitespaces */
+ while (*trimmed == ' ' || *trimmed == '\t') trimmed++;
+
+ /* skip comments and empty tails */
+ if (*trimmed == '#' || *trimmed == '\0') continue;
+
+ /* anything else found on the section line ??? */
+ xwarnx(_("syntax error found in the config - line %d"), line_cnt);
+ }
+
+ switch (section_id) {
+ case SECTION_ID_FIELDS_DISPLAY:
+ token = strtok (trimmed, " \t");
+
+ if (token) {
+ tail = strtok (NULL, " \t");
+
+ if (tail && *tail != '#') {
+ xwarnx(_("syntax error found in the config - line %d"), line_cnt);
+ }
+
+ /* add the field in the list */
+ if (!(cnf_listnode = calloc(1, sizeof *cnf_listnode))) {
+ xwarnx(_("memory allocation failed"));
+ fclose(f);
+ return 0;
+ }
+ snprintf(cnf_listnode -> description, sizeof(cnf_listnode -> description), "%s", token);
+ cnf_listnode -> next = cnf_listhead;
+ cnf_listhead = cnf_listnode;
+ }
+
+ break;
+
+ case SECTION_ID_MAPPING:
+ token = strtok (trimmed, " \t");
+
+ if (token) {
+ tail = strtok (NULL, " \t");
+
+ if (tail && *tail != '#') {
+ xwarnx(_("syntax error found in the config - line %d"), line_cnt);
+ }
+
+ if (!strcmp(token,"ShowPath")) map_desc_showpath = !map_desc_showpath;
+ }
+
+ break;
+
+ case SECTION_ID_UNSUPPORTED:
+ break; /* ignore the content */
+
+ default:
+ xwarnx(_("syntax error found in the config - line %d"), line_cnt);
+ }
+ }
+
+ fclose(f);
+
+ return 1;
+}
+
+
+static int config_create (const char *rc_filename)
+{
+ FILE *f;
+
+ if (rc_filename == NULL)
+ return 0;
+
+ /* check if rc exists */
+ f = fopen(rc_filename, "r");
+
+ if (f) { /* file exists ... let user to delete/remove it first */
+ fclose(f);
+ xwarnx(_("the file already exists - delete or rename it first"));
+ return 0;
+ }
+
+ /* file doesn't exist */
+
+ f = fopen(rc_filename, "w");
+
+ if (!f) return 0; /* can't open the file for writing */
+
+ /* current rc template, might change in the future */
+ fprintf(f,"# pmap's Config File\n");
+ fprintf(f,"\n");
+ fprintf(f,"# All the entries are case sensitive.\n");
+ fprintf(f,"# Unsupported entries are ignored!\n");
+ fprintf(f,"\n");
+ fprintf(f,"[Fields Display]\n");
+ fprintf(f,"\n");
+ fprintf(f,"# To enable a field uncomment its entry\n");
+ fprintf(f,"\n");
+ fprintf(f,"#%s\n", nls_Perm);
+ fprintf(f,"#%s\n", nls_Offset);
+ fprintf(f,"#%s\n", nls_Device);
+ fprintf(f,"#%s\n", nls_Inode);
+ fprintf(f,"#Size\n");
+ fprintf(f,"#Rss\n");
+ fprintf(f,"#Pss\n");
+ fprintf(f,"#Shared_Clean\n");
+ fprintf(f,"#Shared_Dirty\n");
+ fprintf(f,"#Private_Clean\n");
+ fprintf(f,"#Private_Dirty\n");
+ fprintf(f,"#Referenced\n");
+ fprintf(f,"#Anonymous\n");
+ fprintf(f,"#AnonHugePages\n");
+ fprintf(f,"#Swap\n");
+ fprintf(f,"#KernelPageSize\n");
+ fprintf(f,"#MMUPageSize\n");
+ fprintf(f,"#Locked\n");
+ fprintf(f,"#VmFlags\n");
+ fprintf(f,"#%s\n", nls_Mapping);
+ fprintf(f,"\n");
+ fprintf(f,"\n");
+ fprintf(f,"[Mapping]\n");
+ fprintf(f,"\n");
+ fprintf(f,"# to show paths in the mapping column uncomment the following line\n");
+ fprintf(f,"#ShowPath\n");
+ fprintf(f,"\n");
+
+ fclose(f);
+
+ return 1;
+}
+
+
+/* returns rc filename based on the program_invocation_short_name */
+static char *get_default_rc_filename(void)
+{
+ char *rc_filename;
+ int rc_filename_len;
+ const char *homedir;
+
+ homedir = getenv("HOME");
+ if (!homedir) {
+ xwarnx(_("HOME variable undefined"));
+ return NULL;
+ }
+
+ rc_filename_len = snprintf(NULL, 0, "%s/.%src", homedir, program_invocation_short_name);
+
+ rc_filename = (char *) calloc (1, rc_filename_len + 1);
+ if (!rc_filename) {
+ xwarnx(_("memory allocation failed"));
+ return NULL;
+ }
+
+ snprintf(rc_filename, rc_filename_len + 1, "%s/.%src", homedir, program_invocation_short_name);
+
+ return rc_filename;
+}
+
+
+int main(int argc, char **argv)
+{
+ struct pids_fetch *pids_fetch;
+ unsigned *pidlist;
+ int reap_count, user_count;
+ int ret = 0, c, conf_ret;
+ char *rc_filename = NULL;
+
+ static const struct option longopts[] = {
+ {"extended", no_argument, NULL, 'x'},
+ {"device", no_argument, NULL, 'd'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"range", required_argument, NULL, 'A'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {"read-rc", no_argument, NULL, 'c'},
+ {"read-rc-from", required_argument, NULL, 'C'},
+ {"create-rc", no_argument, NULL, 'n'},
+ {"create-rc-to", required_argument, NULL, 'N'},
+ {"show-path", no_argument, NULL, 'p'},
+ {NULL, 0, NULL, 0}
+ };
+
+#ifdef HAVE_PROGRAM_INVOCATION_NAME
+ program_invocation_name = program_invocation_short_name;
+#endif
+ nls_initialize();
+ atexit(close_stdout);
+
+ if (argc < 2)
+ usage(stderr);
+
+ while ((c = getopt_long(argc, argv, "xXrdqA:hVcC:nN:p", longopts, NULL)) != -1)
+ switch (c) {
+ case 'x':
+ x_option = 1;
+ break;
+ case 'X':
+ X_option++;
+ break;
+ case 'r':
+ xwarnx(_("option -r is ignored as SunOS compatibility"));
+ break;
+ case 'd':
+ d_option = 1;
+ break;
+ case 'q':
+ q_option = 1;
+ break;
+ case 'A':
+ range_arguments(optarg);
+ break;
+ case 'h':
+ usage(stdout);
+ case 'V':
+ printf(PROCPS_NG_VERSION);
+ return EXIT_SUCCESS;
+ case 'c':
+ c_option = 1;
+ break;
+ case 'C':
+ C_option = 1;
+ rc_filename = optarg;
+ break;
+ case 'n':
+ n_option = 1;
+ break;
+ case 'N':
+ N_option = 1;
+ rc_filename = optarg;
+ break;
+ case 'p':
+ map_desc_showpath = 1;
+ break;
+ case 'a': /* Sun prints anon/swap reservations */
+ case 'F': /* Sun forces hostile ptrace-like grab */
+ case 'l': /* Sun shows unresolved dynamic names */
+ case 'L': /* Sun shows lgroup info */
+ case 's': /* Sun shows page sizes */
+ case 'S': /* Sun shows swap reservations */
+ default:
+ usage(stderr);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (c_option + C_option + d_option + n_option + N_option + x_option + !!X_option > 1)
+ xerrx(EXIT_FAILURE, _("options -c, -C, -d, -n, -N, -x, -X are mutually exclusive"));
+
+ if ((n_option || N_option) && (q_option || map_desc_showpath))
+ xerrx(EXIT_FAILURE, _("options -p, -q are mutually exclusive with -n, -N"));
+
+ if ((n_option || N_option) && argc > 0)
+ xerrx(EXIT_FAILURE, _("too many arguments"));
+
+ if (N_option) {
+ if (config_create(rc_filename)) {
+ xwarnx(_("rc file successfully created, feel free to edit the content"));
+ return (EXIT_SUCCESS);
+ } else {
+ xerrx(EXIT_FAILURE, _("couldn't create the rc file"));
+ }
+ }
+
+ if (n_option) {
+ rc_filename = get_default_rc_filename();
+
+ if (!rc_filename) return(EXIT_FAILURE);
+
+ conf_ret = config_create(rc_filename); free(rc_filename);
+
+ if (conf_ret) {
+ xwarnx(_("~/.%src file successfully created, feel free to edit the content"), program_invocation_short_name);
+ return (EXIT_SUCCESS);
+ } else {
+ xerrx(EXIT_FAILURE, _("couldn't create ~/.%src"), program_invocation_short_name);
+ }
+ }
+
+ if (argc < 1)
+ xerrx(EXIT_FAILURE, _("argument missing"));
+
+ if (C_option) c_option = 1;
+
+ if (c_option) {
+ if (!C_option) rc_filename = get_default_rc_filename();
+
+ if (!rc_filename) return(EXIT_FAILURE);
+
+ conf_ret = config_read(rc_filename);
+
+ if (!conf_ret) {
+ if (C_option) {
+ xerrx(EXIT_FAILURE, _("couldn't read the rc file"));
+ } else {
+ xwarnx(_("couldn't read ~/.%src"), program_invocation_short_name);
+ free(rc_filename);
+ return(EXIT_FAILURE);
+ }
+ }
+ }
+ if ((size_t)argc >= INT_MAX / sizeof(pid_t))
+ xerrx(EXIT_FAILURE, _("too many arguments"));
+ if (procps_pids_new(&Pids_info, Pid_items, 4))
+ xerrx(EXIT_FAILURE, _("library failed pids statistics"));
+ pidlist = xmalloc(sizeof(pid_t) * argc);
+
+ user_count = 0;
+ while (*argv) {
+ char *walk = *argv++;
+ char *endp;
+ unsigned long pid;
+ if (!strncmp("/proc/", walk, 6)) {
+ walk += 6;
+ /* user allowed to do: pmap /proc/PID */
+ if (*walk < '0' || *walk > '9')
+ continue;
+ }
+ if (*walk < '0' || *walk > '9')
+ usage(stderr);
+ pid = strtoul(walk, &endp, 0);
+ if (pid < 1ul || pid > 0x7ffffffful || *endp)
+ usage(stderr);
+ pidlist[user_count++] = pid;
+ }
+
+ discover_shm_minor();
+
+ if (!(pids_fetch = procps_pids_select(Pids_info, pidlist, user_count, PIDS_SELECT_PID)))
+ xerrx(EXIT_FAILURE, _("library failed pids statistics"));
+
+ for (reap_count = 0; reap_count < pids_fetch->counts->total; reap_count++) {
+ ret |= one_proc(pids_fetch->stacks[reap_count]);
+ }
+
+ free(pidlist);
+ procps_pids_unref(&Pids_info);
+
+ /* cleaning the list used for the -c/-X/-XX modes */
+ for (listnode = listhead; listnode != NULL ; ) {
+ listnode = listnode -> next;
+ free(listhead);
+ listhead = listnode;
+ }
+
+ /* cleaning the list used for the -c mode */
+ for (cnf_listnode = cnf_listhead; cnf_listnode != NULL ; ) {
+ cnf_listnode = cnf_listnode -> next;
+ free(cnf_listhead);
+ cnf_listhead = cnf_listnode;
+ }
+
+ if (reap_count < user_count)
+ /* didn't find all processes asked for */
+ ret |= 42;
+ return ret;
+}