crash-stack: print maps information 12/104912/1
authorŁukasz Stelmach <l.stelmach@samsung.com>
Wed, 14 Dec 2016 14:06:43 +0000 (15:06 +0100)
committerŁukasz Stelmach <l.stelmach@samsung.com>
Wed, 14 Dec 2016 16:18:00 +0000 (17:18 +0100)
Print information about mapped memory regions.

Add stub for printing system and process VM information.

Change-Id: I47b2c5f837620f9d22b3270a75d8f5e663d44c78

src/crash-stack/crash-stack.c

index 2507055..bd5731f 100644 (file)
@@ -16,6 +16,8 @@
  * Authors: Adrian Szyndela <adrian.s@samsung.com>
  *          Łukasz Stelmach <l.stelmach@samsung.com>
  */
+#define _GNU_SOURCE 1
+
 /**
  * @file crash-stack.c
  * @brief This file contains Main module of call stack unwinding program
  * of a crashed program. Crash-stack must be called with proper arguments:
  * either core dump file name or PID of a crashed program.
  */
-#include <stdlib.h>
-#include <stdio.h>
+#include "crash-stack.h"
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
 #include <libelf.h>
+#include <linux/prctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <elfutils/version.h>
 #include <elfutils/libdwfl.h>
 /*
  * In case the program is compiled with core dumps, the license may switch to GPL2.
 #ifdef WITH_CORE_DUMP
 #include <elfutils/libebl.h>
 #endif
-#include <errno.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/prctl.h>
-#include <linux/prctl.h>
-#include "crash-stack.h"
-#include <string.h>
-#include <elfutils/version.h>
-#include <getopt.h>
-#include <sys/ptrace.h>
-#include <sys/uio.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <dirent.h>
-#include <sys/syscall.h>
+
+#define BUF_SIZE (BUFSIZ)
+#define HEXA 16
+#define PERM_LEN 5
+#define ADDR_LEN 16
+#define STR_ANONY "[anony]"
+#define STR_ANONY_LEN 8
 
 static siginfo_t __siginfo;
 static FILE *outputfile = NULL;                ///< global output stream
@@ -76,6 +87,22 @@ const struct option opts[] = {
        { 0, 0, 0, 0 }
 };
 
+/**
+ * @brief container for information from /proc/PID/maps
+ */
+struct addr_node {
+       uintptr_t startaddr;
+       uintptr_t endaddr;
+       char perm[PERM_LEN];
+       char *fpath;
+       struct addr_node *next;
+};
+
+/* helper functions for reading /proc/PID/maps */
+static struct addr_node *get_addr_list_from_maps(int fd);
+static void free_all_nodes(struct addr_node *start);
+static char *fgets_fd(char *str, int len, int fd);
+
 /*
  * __cxa_demangle() is taken from libstdc++, however there is no header that we
  * can take a declaration from. Importing through 'extern' allows using it.
@@ -855,6 +882,166 @@ static void __crash_stack_print_threads(FILE* outputfile, pid_t pid, pid_t tid)
 }
 
 /**
+ * @brief Print information about mapped memory regions
+ *
+ * @param outputfile File handle for printing report.
+ * @param pid PID of the inspected process
+ */
+static void __crash_stack_print_maps(FILE* outputfile, pid_t pid)
+{
+       char file_path[PATH_MAX];
+       struct addr_node *head = NULL;
+       struct addr_node *t_node;
+       int fd;
+
+       snprintf(file_path, PATH_MAX, "/proc/%d/maps", pid);
+
+       if ((fd = open(file_path, O_RDONLY)) < 0) {
+               fprintf(errfile, "[crash-stack] cannot open %s\n", file_path);
+        } else {
+                /* parsing the maps to get code segment address*/
+                head = get_addr_list_from_maps(fd);
+                close(fd);
+        }
+       if (head == NULL) {
+               return;
+       }
+
+       t_node = head;
+       fprintf(outputfile, "\nMaps Information\n");
+       while (t_node) {
+               if (!strncmp(STR_ANONY, t_node->fpath, STR_ANONY_LEN)) {
+                       t_node = t_node->next;
+               } else {
+                       printf( "%16lx %16lx %s %s\n",
+                               (unsigned long)t_node->startaddr,
+                               (unsigned long)t_node->endaddr,
+                               t_node->perm, t_node->fpath);
+                       t_node = t_node->next;
+               }
+       }
+       fprintf(outputfile, "End of Maps Information\n");
+       free_all_nodes(head);
+}
+
+static struct addr_node *get_addr_list_from_maps(int fd)
+{
+        int fpath_len, result;
+        uintptr_t saddr;
+        uintptr_t eaddr;
+        char perm[PERM_LEN];
+        char path[PATH_MAX];
+        char addr[ADDR_LEN * 2 + 2];
+        char linebuf[BUF_SIZE];
+        struct addr_node *head = NULL;
+        struct addr_node *tail = NULL;
+        struct addr_node *t_node = NULL;
+
+        /* parsing the maps to get executable code address */
+        while (fgets_fd(linebuf, BUF_SIZE, fd) != NULL) {
+                memset(path, 0, PATH_MAX);
+                result = sscanf(linebuf, "%s %s %*s %*s %*s %s ", addr, perm, path);
+                if (result < 0)
+                        continue;
+                perm[PERM_LEN - 1] = 0;
+                /* rwxp */
+                if ((perm[2] == 'x' && path[0] == '/') ||
+                   (perm[1] == 'w' && path[0] != '/'))
+               {
+                       char* addr2 = strchr(addr, '-');
+                       *(addr2++) = '\0';
+                       /* add addr node to list */
+                       saddr = strtoul(addr, NULL, HEXA);
+                       /* ffff0000-ffff1000 */
+                       eaddr = strtoul(addr2, NULL, HEXA);
+                       /* make node and attach to the list */
+                       t_node = (struct addr_node *)mmap(0, sizeof(struct addr_node),
+                                                         PROT_READ | PROT_WRITE,
+                                                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+                       if (t_node == NULL) {
+                               fprintf(stderr, "error : mmap\n");
+                               return NULL;
+                       }
+                       memcpy(t_node->perm, perm, PERM_LEN);
+                       t_node->startaddr = saddr;
+                       t_node->endaddr = eaddr;
+                       t_node->fpath = NULL;
+                       fpath_len = strlen(path);
+                       if (fpath_len > 0) {
+                               t_node->fpath = (char *)mmap(0, fpath_len + 1,
+                                                            PROT_READ | PROT_WRITE,
+                                                            MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+                               memset(t_node->fpath, 0, fpath_len + 1);
+                               memcpy(t_node->fpath, path, fpath_len);
+                       } else {
+                               t_node->fpath = (char *)mmap(0, STR_ANONY_LEN,
+                                                            PROT_READ | PROT_WRITE,
+                                                            MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+                               memset(t_node->fpath, 0, STR_ANONY_LEN);
+                               memcpy(t_node->fpath, STR_ANONY, STR_ANONY_LEN);
+                       }
+                       t_node->next = NULL;
+                       if (head == NULL) {
+                               head = t_node;
+                               tail = t_node;
+                       } else {
+                               tail->next = t_node;
+                               tail = t_node;
+                       }
+               }
+       }
+       return head;
+}
+
+static void free_all_nodes(struct addr_node *start)
+{
+       struct addr_node *t_node, *n_node;
+       int fpath_len;
+
+       if (start == NULL)
+               return;
+       t_node = start;
+       n_node = t_node->next;
+       while (t_node) {
+               if (t_node->fpath != NULL) {
+                       fpath_len = strlen(t_node->fpath);
+                       munmap(t_node->fpath, fpath_len + 1);
+               }
+               munmap(t_node, sizeof(struct addr_node));
+               if (n_node == NULL)
+                       break;
+               t_node = n_node;
+               n_node = n_node->next;
+       }
+}
+
+static char *fgets_fd(char *str, int len, int fd)
+{
+        char ch;
+        register char *cs;
+        int num = 0;
+
+        cs = str;
+        while (--len > 0 && (num = read(fd, &ch, 1) > 0)) {
+                if ((*cs++ = ch) == '\n')
+                        break;
+        }
+        *cs = '\0';
+        return (num == 0 && cs == str) ? NULL : str;
+}
+
+/**
+ * @brief Print process and system memory information
+ *
+ * @param outputfile File handle for printing report.
+ * @param pid PID of the inspected process
+ */
+static void __crash_stack_print_meminfo(FILE* outputfile, pid_t pid)
+{
+       fprintf(outputfile, "\nMemory Information\n");
+}
+
+/**
  * @brief Main function.
  *
  * Main module accepts two forms of launching:
@@ -969,9 +1156,15 @@ int main(int argc, char **argv)
        /* Print registers */
        _crash_stack_print_regs(outputfile);
 
+       /* Memory information */
+       __crash_stack_print_meminfo(outputfile, pid);
+
        /* Threads */
        __crash_stack_print_threads(outputfile, pid, tid);
 
+       /* Maps Information */
+       __crash_stack_print_maps(outputfile, pid);
+
        /* Print the results */
        __print_callstack(&callstack, tid);