[minicoredumper] [PATCH 03/14] minicoredumper: read stack pointer from registers

Mateusz Moscicki m.moscicki2 at partner.samsung.com
Tue May 21 14:52:39 CEST 2019


From: Łukasz Stelmach <l.stelmach at samsung.com>

After stopping the reporting esp/eip values in Linux kernel
(0a1eb2d474ed) it is no longer possible to obtain the values of an SP
register for non-crashing threads of a crashing process. This and
following commits use architecture specific code to read the values from
NT_PRSTATUS notes in a core dump.

Change-Id: I6d5a7ef9275fc301ec11a795fb4332a7e5586c01
---
 src/minicoredumper/corestripper.c | 172 +++++++++++++-------------------------
 src/minicoredumper/corestripper.h |   3 +-
 2 files changed, 60 insertions(+), 115 deletions(-)

diff --git a/src/minicoredumper/corestripper.c b/src/minicoredumper/corestripper.c
index 350927f..8ebcb4b 100644
--- a/src/minicoredumper/corestripper.c
+++ b/src/minicoredumper/corestripper.c
@@ -28,6 +28,7 @@
 #include <sys/procfs.h>
 #include <sys/syscall.h>
 #include <sys/ptrace.h>
+#include <sys/user.h>
 #include <linux/futex.h>
 #include <elfutils/version.h>
 
@@ -371,14 +372,11 @@ static int copy_file(const char *dest, const char *src)
 
 static int get_task_list(struct dump_info *di)
 {
-	pid_t *pidlist = NULL;
 	struct dirent *de;
 	int count = 0;
 	char buf[64];
 	int err = 0;
-	int pid;
 	DIR *d;
-	int i;
 
 	di->tsks = NULL;
 	di->ntsks = 0;
@@ -405,51 +403,16 @@ static int get_task_list(struct dump_info *di)
 	if (count == 0)
 		goto out;
 
-	pidlist = calloc(count, sizeof(pid_t));
-	if (!pidlist) {
+	di->tsks = calloc(count, sizeof(struct elf_prstatus));
+	if (!di->tsks) {
 		err = 1;
 		goto out;
 	}
 
-	rewinddir(d);
-
-	/* read the actual tasks */
-	for (i = 0; i < count; ) {
-		de = readdir(d);
-		if (!de) {
-			err = 1;
-			goto out;
-		}
-
-		/* ignore hidden files */
-		if (de->d_name[0] == '.')
-			continue;
-
-		if (sscanf(de->d_name, "%d", &pid) != 1) {
-			err = 1;
-			goto out;
-		}
-
-		pidlist[i] = pid;
-
-		i++;
-	}
-
-	/* make sure we really have exactly "count" tasks */
-	if (readdir(d) != NULL) {
-		err = 1;
-		goto out;
-	}
-
-	di->tsks = pidlist;
-	pidlist = NULL;
 	di->ntsks = count;
 out:
 	closedir(d);
 
-	if (pidlist)
-		free(pidlist);
-
 	return err;
 }
 
@@ -1870,52 +1833,27 @@ static void cleanup_di(struct dump_info *di)
 	}
 }
 
-static int get_stack_pointer(pid_t pid, unsigned long *addr)
+// Since the commit 0a1eb2d474ed kernel has stopped reporting eip/esp in
+// /proc/<pid>/stat, so we must read these values from the task memory
+static void get_stack_pointer(struct dump_info *di, int i, unsigned long *addr)
 {
-#define STAT_LINE_MAXSIZE 4096
-	FILE *f = NULL;
-	int err = -1;
-	char *buf;
-	char *p;
-	int i;
-
-	/* create a buffer large enough for stat line */
-	buf = malloc(STAT_LINE_MAXSIZE);
-	if (!buf)
-		goto out_err;
-
-	/* open stat file */
-	snprintf(buf, STAT_LINE_MAXSIZE, "/proc/%d/stat", pid);
-	f = fopen(buf, "r");
-	if (!f)
-		goto out_err;
-
-	/* read line */
-	if (fgets(buf, STAT_LINE_MAXSIZE, f) == NULL)
-		goto out_err;
-
-	/* find 29th item: man proc(5) */
-	p = buf;
-	for (i = 0; i < 28; i++) {
-		p = strchr(p, ' ');
-		if (!p)
-			goto out_err;
-		p++;
-	}
-
-	/* read stack pointer */
-	if (sscanf(p, "%lu ", addr) != 1)
-		goto out_err;
-
-	err = 0;
-out_err:
-	if (f)
-		fclose(f);
-	if (buf)
-		free(buf);
+#if defined(__arm__)
+	struct user_regs *u_reg = (struct user_regs *)&di->tsks[i].pr_reg;
+#else
+	struct user_regs_struct *u_reg = (struct user_regs_struct *)&di->tsks[i].pr_reg;
+#endif
 
-	return err;
-#undef STAT_LINE_MAXSIZE
+#if defined(__x86_64__)
+	*addr = u_reg->rsp;
+#elif defined(__i386__)
+	*addr = u_reg->esp;
+#elif defined(__arm__)
+	*addr = u_reg->uregs[13];
+#elif defined(__aarch64__)
+	*addr = u_reg->sp;
+#else
+#error Unsupported architecture
+#endif
 }
 
 static struct core_vma *get_next_vma_range(struct dump_info *di,
@@ -1964,6 +1902,8 @@ static int dump_vma(struct dump_info *di, unsigned long start, size_t len,
 
 	end = start + len;
 
+	info("dumping %p-%p", start, end);
+
 	tmp = get_next_vma_range(di, start, end, di->vma);
 	if (!tmp) {
 		info("vma not found start=0x%lx! bad recept or internal bug!",
@@ -2002,6 +1942,12 @@ static int dump_vma(struct dump_info *di, unsigned long start, size_t len,
 			info("dump: %s: %zu bytes @ 0x%lx", desc ? desc : "",
 			     len, dump_start);
 
+			info ("add_core_data %ld %ld %ld %p",
+				tmp->file_off + dump_start - tmp->start,
+				len,
+				di->mem_fd,
+				dump_start);
+
 			err = add_core_data(di, tmp->file_off + dump_start -
 						tmp->start, len, di->mem_fd,
 					    dump_start);
@@ -2022,6 +1968,7 @@ static int note_cb(struct dump_info *di, Elf *elf, GElf_Phdr *phdr)
 {
 	size_t offset = 0;
 	Elf_Data *data;
+	int i;
 
 	data = elf_getdata_rawchunk(elf, phdr->p_offset, phdr->p_filesz,
 				    ELF_T_NHDR);
@@ -2031,7 +1978,9 @@ static int note_cb(struct dump_info *di, Elf *elf, GElf_Phdr *phdr)
 		return -1;
 	}
 
-	while (offset < data->d_size) {
+	di->first_pid = 0;
+	i = 0;
+	while (i < di->ntsks && offset < data->d_size) {
 		const struct elf_prstatus *status;
 		size_t name_offset;
 		size_t desc_offset;
@@ -2053,14 +2002,14 @@ static int note_cb(struct dump_info *di, Elf *elf, GElf_Phdr *phdr)
 
 		status = (const struct elf_prstatus *)desc;
 
-		di->first_pid = status->pr_pid;
+		if (!di->first_pid)
+			di->first_pid = status->pr_pid;
 
-		/* success, we can stop */
-		return 1;
+		memcpy(&di->tsks[i], status, sizeof(struct elf_prstatus));
+		i++;
 	}
 
-	/* we found nothing, keep looking */
-	return 0;
+	return (di->first_pid != 0);
 }
 
 /*
@@ -2074,14 +2023,12 @@ static int dump_stacks(struct dump_info *di)
 	size_t len;
 	int i;
 
-	if (di->cfg->prog_config.stack.first_thread_only) {
-		GElf_Phdr type;
+	GElf_Phdr type;
 
-		/* find and set the first task */
-		memset(&type, 0, sizeof(type));
-		type.p_type = PT_NOTE;
-		do_elf_ph_parse(di, &type, note_cb);
-	}
+	/* find and set task statuses*/
+	memset(&type, 0, sizeof(type));
+	type.p_type = PT_NOTE;
+	do_elf_ph_parse(di, &type, note_cb);
 
 	if (di->first_pid)
 		info("first thread: %i", di->first_pid);
@@ -2089,21 +2036,18 @@ static int dump_stacks(struct dump_info *di)
 	for (i = 0; i < di->ntsks; i++) {
 		/* skip this task if we should only dump the
 		 * first task and we know the first task */
-		if (di->first_pid && (di->first_pid != di->tsks[i]))
+		if (di->cfg->prog_config.stack.first_thread_only &&
+		    di->first_pid && (di->first_pid != di->tsks[i].pr_pid))
 			continue;
 
 		/* grab the stack pointer */
-		if (get_stack_pointer(di->tsks[i], &stack_addr) != 0) {
-			info("unable to find thread #%d's (%d) stack pointer",
-			     i + 1, di->tsks[i]);
-			continue;
-		}
+		get_stack_pointer(di, i, &stack_addr);
 
 		/* find the vma containing the stack */
 		tmp = get_vma_pos(di, stack_addr);
 		if (!tmp) {
 			info("unable to find thread #%d's (%d) stack", i + 1,
-			     di->tsks[i]);
+			     di->tsks[i].pr_pid);
 			continue;
 		}
 
@@ -2114,13 +2058,13 @@ static int dump_stacks(struct dump_info *di)
 		max_len = di->cfg->prog_config.stack.max_stack_size;
 		if (max_len && len > max_len) {
 			info("stack[%d] is too large (%zu bytes), truncating "
-			     "to %zu bytes", di->tsks[i], len, max_len);
+			     "to %zu bytes", di->tsks[i].pr_pid, len, max_len);
 			len = max_len;
 		}
 
 		/* dump the bottom part of stack in use */
 		dump_vma(di, stack_addr, len, 0, "stack[%d]",
-			 di->tsks[i]);
+			 di->tsks[i].pr_pid);
 	}
 
 	return 0;
@@ -2844,13 +2788,13 @@ static void copy_proc_files(struct dump_info *di, int tasks, const char *name,
 
 	for (i = 0 ; i < di->ntsks; i++) {
 		snprintf(path, size, "%s/proc/%d/task/%d", di->dst_dir,
-			 di->cmd_params->pid, di->tsks[i]);
+			 di->cmd_params->pid, di->tsks[i].pr_pid);
 		mkdir(path, 0700);
 
 		/* handle the normal task case */
 		if (!do_fds) {
 			snprintf(path, size, "%s/proc/%d/task/%d/%s",
-				 di->dst_dir, di->cmd_params->pid, di->tsks[i], name);
+				 di->dst_dir, di->cmd_params->pid, di->tsks[i].pr_pid, name);
 
 			if (link)
 				copy_link(path, path + base_len);
@@ -2861,7 +2805,7 @@ static void copy_proc_files(struct dump_info *di, int tasks, const char *name,
 
 		/* special case: copy the symlinks in the fd directory */
 		snprintf(path, size, "%s/proc/%d/task/%d/fd", di->dst_dir,
-			 di->cmd_params->pid, di->tsks[i]);
+			 di->cmd_params->pid, di->tsks[i].pr_pid);
 		mkdir(path, 0700);
 
 		d = opendir(path + base_len);
@@ -2878,7 +2822,7 @@ static void copy_proc_files(struct dump_info *di, int tasks, const char *name,
 				continue;
 
 			snprintf(path, size, "%s/proc/%d/task/%d/fd/%s",
-				 di->dst_dir, di->cmd_params->pid, di->tsks[i],
+				 di->dst_dir, di->cmd_params->pid, di->tsks[i].pr_pid,
 				 de->d_name);
 
 			copy_link(path, path + base_len);
@@ -3411,10 +3355,6 @@ static void do_dump(struct dump_info *di)
 		dump_maps(di, 1);
 	}
 
-	/* copy intersting /proc data (if configured) */
-	if (di->cfg->prog_config.write_proc_info)
-		write_proc_info(di);
-
 	/* Get shared object list. This is necessary for sym_address() to work.
 	 * This function will also dump the auxv data (if configured). */
 	get_so_list(di);
@@ -3423,6 +3363,10 @@ static void do_dump(struct dump_info *di)
 	if (di->cfg->prog_config.stack.dump_stacks)
 		dump_stacks(di);
 
+	/* copy intersting /proc data (if configured) */
+	if (di->cfg->prog_config.write_proc_info)
+		write_proc_info(di);
+
 	/* dump the pthread list (if configured) */
 	if (di->cfg->prog_config.dump_pthread_list)
 		get_pthread_list(di);
diff --git a/src/minicoredumper/corestripper.h b/src/minicoredumper/corestripper.h
index 22da645..1e6c918 100644
--- a/src/minicoredumper/corestripper.h
+++ b/src/minicoredumper/corestripper.h
@@ -10,6 +10,7 @@
 #include <stdio.h>
 #include <libelf.h>
 #include <gelf.h>
+#include <sys/procfs.h>
 
 struct core_data;
 
@@ -80,7 +81,7 @@ struct dump_info {
 	/* from core_pattern */
 	struct cmd_parameters *cmd_params;
 
-	pid_t *tsks;
+	struct elf_prstatus *tsks;
 	int ntsks;
 
 	unsigned long vma_start;
-- 
2.7.4




More information about the minicoredumper mailing list