| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #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 "proc/escape.h" |
| #include "xalloc.h" |
| #include "proc/readproc.h" |
| #include "proc/version.h" |
| |
| 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); |
| |
| |
| |
| nls_Address = _("Address"); |
| nls_Offset = _("Offset"); |
| nls_Device = _("Device"); |
| nls_Mapping = _("Mapping"); |
| |
| |
| |
| nls_Perm = _("Perm"); |
| nls_Inode = _("Inode"); |
| |
| |
| 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 KLONG 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 char cmdbuf[512]; |
| |
| static unsigned KLONG range_low; |
| static unsigned KLONG range_high = ~0ull; |
| |
| static int c_option; |
| static int C_option; |
| static int d_option; |
| static int n_option; |
| static int N_option; |
| static int q_option; |
| static int x_option; |
| static int X_option; |
| |
| static int map_desc_showpath; |
| |
| static unsigned shm_minor = ~0u; |
| |
| static void discover_shm_minor(void) |
| { |
| void *addr; |
| int shmid; |
| char mapbuf_b[256]; |
| |
| if (!freopen("/proc/self/maps", "r", stdin)) |
| return; |
| |
| |
| shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666); |
| if (shmid == -1) |
| |
| return; |
| |
| addr = shmat(shmid, NULL, SHM_RDONLY); |
| if (addr == (void *)-1) |
| goto out_destroy; |
| |
| while (fgets(mapbuf_b, sizeof mapbuf_b, stdin)) { |
| char perms[32]; |
| |
| char *tmp; |
| unsigned KLONG start, end; |
| unsigned long long file_offset, inode; |
| unsigned dev_major, dev_minor; |
| if (sscanf(mapbuf_b, "%" KLF "x-%" KLF "x %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: |
| if (shmctl(shmid, IPC_RMID, NULL) && errno != EINVAL) |
| perror(_("shared memory remove")); |
| |
| return; |
| } |
| |
| static const char *mapping_name(const proc_t * p, unsigned KLONG addr, |
| unsigned KLONG 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 ((p->start_stack >= addr) && (p->start_stack <= addr + len)) |
| cp = _(" [ stack ]"); |
| return cp; |
| } |
| |
| |
| #define DETAIL_LENGTH 32 |
| #define DETL "31" |
| #define NUM_LENGTH 21 |
| #define NUML "20" |
| #define VMFLAGS_LENGTH 128 |
| #define VMFL "127" |
| |
| 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, *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; |
| } |
| |
| |
| static int is_enabled (const char *s) |
| { |
| if (X_option == 1) return !is_unimportant(s); |
| |
| if (c_option) { |
| |
| 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) { |
| |
| 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); |
| |
| if (nfields < 6) |
| xerrx(EXIT_FAILURE, _("Unknown format in smaps file!")); |
| |
| c = mapbuf[strlen(mapbuf) - 1]; |
| while (c != '\n') { |
| ret = fgets(mapbuf, sizeof mapbuf, f); |
| if (!ret || !mapbuf[0]) |
| xerrx(EXIT_FAILURE, _("Unknown format in smaps file!")); |
| c = mapbuf[strlen(mapbuf) - 1]; |
| } |
| |
| |
| 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; |
| |
| while (ret != NULL && nfields == 2) { |
| |
| if (!is_enabled(detail_desc)) goto loop_end; |
| |
| |
| if (listnode == NULL) { |
| assert(firstmapping == 2); |
| listnode = calloc(1, sizeof *listnode); |
| if (listhead == NULL) { |
| assert(listtail == NULL); |
| listhead = listnode; |
| } else { |
| listtail->next = listnode; |
| } |
| listtail = listnode; |
| |
| strcpy(listnode->description, detail_desc); |
| if (!q_option) listnode->max_width = strlen(detail_desc); |
| else listnode->max_width = 0; |
| } else { |
| |
| 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, "%"KLF"u", &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; |
| } |
| |
| |
| 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) { |
| if (ret == NULL) { |
| firstmapping = 1; |
| fseek(f, 0, SEEK_SET); |
| ret = fgets(mapbuf, sizeof mapbuf, f); |
| |
| if (!q_option) { |
| |
| for (listnode=listhead; listnode!=NULL; listnode=listnode->next) { |
| maxw_ = integer_width(listnode->total); |
| if (maxw_ > listnode->max_width) |
| listnode->max_width = maxw_; |
| } |
| } |
| |
| } |
| } else { |
| |
| |
| |
| 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"); |
| } |
| |
| |
| printf("%*s", maxw1, start); |
| |
| 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; |
| |
| } |
| } |
| |
| if (!q_option && listhead!=NULL) { |
| |
| footer_gap = maxw1 + 1; |
| 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); |
| |
| fputs("KB \n", stdout); |
| } |
| |
| } |
| |
| static int one_proc(const proc_t * p) |
| { |
| char buf[32]; |
| FILE *fp; |
| unsigned long total_shared = 0ul; |
| unsigned long total_private_readonly = 0ul; |
| unsigned long total_private_writeable = 0ul; |
| unsigned KLONG start = 0; |
| unsigned KLONG diff = 0; |
| unsigned KLONG end = 0; |
| 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; |
| |
| |
| |
| |
| int maxcmd = 0xfffff; |
| |
| escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd, |
| ESC_ARGS | ESC_BRACKETS); |
| printf("%u: %s\n", p->tgid, cmdbuf); |
| |
| if (x_option || X_option || c_option) { |
| snprintf(buf, sizeof buf, "/proc/%u/smaps", p->tgid); |
| if ((fp = fopen(buf, "r")) == NULL) |
| return 1; |
| } else { |
| snprintf(buf, sizeof buf, "/proc/%u/maps", p->tgid); |
| 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(KLONG) == 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(KLONG) == 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)) { |
| |
| char *tmp; |
| unsigned long long file_offset, inode; |
| unsigned dev_major, dev_minor; |
| unsigned long long smap_value; |
| char smap_key[21]; |
| |
| |
| if (mapbuf[0] >= 'A' && mapbuf[0] <= 'Z') { |
| |
| 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) { |
| |
| if (cp2) printf("%0*" KLF "x %*lu %*llu %*llu %*s %s\n", |
| maxw1, start, |
| maxw2, (unsigned long)(diff >> 10), |
| maxw3, rss, |
| maxw4, (private_dirty + shared_dirty), |
| maxw5, perms, |
| cp2); |
| |
| rss = shared_dirty = private_dirty = 0ull; |
| start = diff = end = 0; |
| perms[0] = '\0'; |
| cp2 = NULL; |
| continue; |
| } |
| } |
| |
| continue; |
| } |
| if (sscanf(mapbuf, "%" KLF "x-%" KLF "x %31s %llx %x:%x %llu", &start, |
| &end, perms, &file_offset, &dev_major, &dev_minor, &inode) != 7) |
| continue; |
| |
| if (end - 1 < range_low) |
| continue; |
| if (range_high < start) |
| break; |
| |
| tmp = strchr(mapbuf, '\n'); |
| if (tmp) |
| *tmp = '\0'; |
| tmp = mapbuf; |
| while (*tmp) { |
| if (!isprint(*tmp)) |
| *tmp = '?'; |
| tmp++; |
| } |
| |
| diff = end - start; |
| 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; |
| } |
| |
| |
| |
| |
| perms[4] = '-'; |
| perms[5] = '\0'; |
| |
| if (x_option) { |
| cp2 = |
| mapping_name(p, start, diff, mapbuf, map_desc_showpath, dev_major, |
| dev_minor, inode); |
| |
| continue; |
| } |
| if (d_option) { |
| const char *cp = |
| mapping_name(p, start, diff, mapbuf, map_desc_showpath, dev_major, |
| dev_minor, inode); |
| printf("%0*" KLF "x %*lu %*s %0*llx %*.*s%03x:%05x %s\n", |
| maxw1, start, |
| 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, diff, mapbuf, map_desc_showpath, dev_major, |
| dev_minor, inode); |
| printf((sizeof(KLONG) == 8) |
| ? "%016" KLF "x %6luK %s %s\n" |
| : "%08lx %6luK %s %s\n", |
| start, (unsigned long)(diff >> 10), perms, cp); |
| } |
| |
| } |
| fclose(fp); |
| if (!q_option) { |
| if (x_option) { |
| if (sizeof(KLONG) == 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(KLONG) == 8) |
| |
| |
| printf(_(" total %16ldK\n"), |
| (total_shared + total_private_writeable + |
| total_private_readonly) >> 10); |
| else |
| |
| |
| printf(_(" total %8ldK\n"), |
| (total_shared + total_private_writeable + |
| total_private_readonly) >> 10); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void range_arguments(const char *optarg) |
| { |
| char *arg1; |
| char *arg2; |
| char *const copy = xstrdup(optarg); |
| if (!copy) |
| goto fail; |
| arg1 = copy; |
| arg2 = strchr(arg1, ','); |
| if (arg2) |
| *arg2++ = '\0'; |
| else |
| arg2 = arg1; |
| if (*arg1) |
| range_low = STRTOUKL(arg1, &arg1, 16); |
| if (*arg2) |
| range_high = STRTOUKL(arg2, &arg2, 16); |
| if (*arg1 || *arg2) |
| goto fail; |
| free(copy); |
| return; |
| fail: |
| xerrx(EXIT_FAILURE, "%s: '%s'", _("failed to parse argument"), optarg); |
| } |
| |
| |
| #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; |
| |
| line_cnt = 0; |
| section_id = SECTION_ID_NONE; |
| |
| while (fgets (line_buf, MAX_CNF_LINE_LEN + 1, f)) { |
| |
| line_cnt++; |
| |
| |
| length = strlen(line_buf); |
| if (length > 0 && line_buf[length - 1] == '\n') { |
| line_buf[length - 1] = '\0'; |
| } else if (length == MAX_CNF_LINE_LEN) { |
| xwarnx(_("config line too long - line %d"), line_cnt); |
| |
| while (fgets (tmp_buf, MAX_CNF_LINE_LEN + 1, f) && |
| (length = strlen(tmp_buf))>0 && |
| tmp_buf[length - 1] != '\n') ; |
| } |
| |
| |
| trimmed = line_buf; |
| while (*trimmed == ' ' || *trimmed == '\t') trimmed++; |
| |
| |
| if (*trimmed == '#' || *trimmed == '\0') continue; |
| |
| if (*trimmed == '[') { |
| 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); |
| } |
| } |
| |
| |
| while (*trimmed == ' ' || *trimmed == '\t') trimmed++; |
| |
| |
| if (*trimmed == '#' || *trimmed == '\0') continue; |
| |
| |
| 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); |
| } |
| |
| |
| cnf_listnode = calloc(1, sizeof *cnf_listnode); |
| 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; |
| |
| default: |
| xwarnx(_("syntax error found in the config - line %d"), line_cnt); |
| } |
| } |
| |
| fclose(f); |
| |
| return 1; |
| } |
| |
| |
| static int config_create (char *rc_filename) |
| { |
| FILE *f; |
| |
| |
| f = fopen(rc_filename, "r"); |
| |
| if (f) { |
| fclose(f); |
| xwarnx(_("the file already exists - delete or rename it first")); |
| return 0; |
| } |
| |
| |
| |
| f = fopen(rc_filename, "w"); |
| |
| if (!f) return 0; |
| |
| |
| 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; |
| } |
| |
| |
| |
| 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) |
| { |
| pid_t *pidlist; |
| unsigned count = 0; |
| PROCTAB *PT; |
| proc_t p; |
| 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': |
| case 'F': |
| case 'l': |
| case 'L': |
| case 's': |
| case 'S': |
| 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")); |
| pidlist = xmalloc(sizeof(pid_t) * (argc+1)); |
| |
| while (*argv) { |
| char *walk = *argv++; |
| char *endp; |
| unsigned long pid; |
| if (!strncmp("/proc/", walk, 6)) { |
| walk += 6; |
| |
| 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[count++] = pid; |
| } |
| |
| discover_shm_minor(); |
| |
| memset(&p, '\0', sizeof(p)); |
| |
| pidlist[count] = 0; |
| PT = openproc(PROC_FILLSTAT | PROC_FILLARG | PROC_PID, pidlist); |
| while (readproc(PT, &p)) { |
| ret |= one_proc(&p); |
| count--; |
| } |
| closeproc(PT); |
| free(pidlist); |
| |
| |
| for (listnode = listhead; listnode != NULL ; ) { |
| listnode = listnode -> next; |
| free(listhead); |
| listhead = listnode; |
| } |
| |
| |
| for (cnf_listnode = cnf_listhead; cnf_listnode != NULL ; ) { |
| cnf_listnode = cnf_listnode -> next; |
| free(cnf_listhead); |
| cnf_listhead = cnf_listnode; |
| } |
| |
| if (count) |
| |
| ret |= 42; |
| return ret; |
| } |