* 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
{ 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.
}
/**
+ * @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:
/* 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);