Skip to contentSkip to conversation

Non disruptive application core dump infrastructure

RFC PATCH In review Janani Venkataraman 6 participants 44 messages Mar 20, 2014 – Jul 3, 2014

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover Non disruptive application core dump infrastructure

Hi all,

The following series implements an infrastructure for capturing the core of an application without disrupting its process.

Kernel Space Approach:

1) Posted an RFD to LKML explaining the various kernel-methods being analysed.

https://lkml.org/lkml/2013/9/3/122

2) Went ahead to implement the same using the task_work_add approach and posted an RFC to LKML.

http://lwn.net/Articles/569534/

Based on the responses, the present approach implements the same in User-Space.

User Space Approach:

We didn't adopt the CRIU approach because our method would give us a head start, as all that the distro would need is the PTRACE_functionality and nothing more which is available from kernel versions 3.4 and above.

Basic Idea of User Space:

1) The threads are held using PTRACE_SEIZE and PTRACE_INTERRUPT.

2) The dump is then taken using the following:
1) The register sets namely general purpose, floating point and the arch specific register sets are collected through PTRACE_GETREGSET calls by passing the appropriate register type as parameter.
2) The virtual memory maps are collected from /proc/pid/maps.
3) The auxiliary vector is collected from /proc/pid/auxv.
4) Process state information for filling the notes such as PRSTATUS and PRPSINFO are collected from /proc/pid/stat and /proc/pid/status.
5) The actual memory is read through process_vm_readv syscall as suggested by Andi Kleen.
6) Command line arguments are collected from /proc/pid/cmdline

3) The threads are then released using PTRACE_DETACH.

Self Dump:

A self dump is implemented with the following approach which was adapted from CRIU:

Gencore Daemon

The programs can request a dump using gencore() API, provided through libgencore. This is implemented through a daemon which listens on a UNIX File socket. The daemon is started immediately post installation.

We have provided service scripts for integration with systemd.

NOTE:

On systems with systemd, we could make use of socket option, which will avoid the need for running the gencore daemon always. The systemd can wait on the socket for requests and trigger the daemon as and when required. However, since the systemd socket APIs are not exported yet, we have disabled the supporting code for this feature.

libgencore:

1) The client interface is a standard library call. All that the dump requester does is open the library and call the gencore() API and the dump will be generated in the path specified(relative/absolute).

To Do:

1) Presently we wait indefinitely for the all the threads to seize. We can add a time-out to decide how much time we need to wait for the threads to be seized. This can be passed as command line argument in the case of a third party dump and in the case of the self-dump through the library call. We need to work on how much time to wait.

2) Like mentioned before, the systemd socket APIs are not exported yet and hence this option is disabled now. Once these API's are available we can enable the socket option.

We would like to push this to one of the following packages:
a) util-linux
b) coreutils
c) procps-ng

We are not sure which one would suit this application the best. Please let us know your views on the same.

Patches 1 - 16 implements the dump generation.

Patches 17 - 24 implements the daemon approach.

Patch 25 implements the systemd socket approach.

Patches 26-27 implements the client-interface library.

Patches 28-33 handles the building and other packaging aspects.

Please let us know your reviews and comments.

Thanks.

Janani Venkataraman (33):
Configure and Make files
Validity of arguments
Process Status
Hold threads
Fetching Memory maps
Check ELF class
Do elf_coredump
Fills elf header
Adding notes infrastructure
Populates PRPS info
Populate AUXV
Fetch File maps
Fetching thread specific Notes
Populating Program Headers
Updating Offset
Writing to core file
Daemonizing the Process
Socket operations
Block till request
Handling Requests
Get Clients PID
Dump the task
Handling SIG TERM of the daemon
Handling SIG TERM of the child
Systemd Socket ID retrieval
[libgencore] Setting up Connection
[libgencore] Request for dump
Man pages
Automake files for the doc folder
README, COPYING, Changelog
Spec file
Socket and Service files.
Support check

 COPYING            |   24 ++
 COPYING.LIBGENCORE |   24 ++
 Changelog          |    7 
 Makefile.am        |   22 +
 README             |  108 +++++++
 configure.ac       |    8 +
 doc/Makefile.am    |    2 
 doc/gencore.1      |   31 ++
 doc/gencore.3      |   28 ++
 gencore.service    |    9 +
 gencore.socket     |   10 +
 gencore.spec.in    |   88 ++++++
 [email protected]   |    9 +
 libgencore.pc.in   |    8 +
 src/Makefile.am    |   13 +
 src/client.c       |  121 ++++++++
 src/coredump.c     |  764 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/coredump.h     |   74 +++++
 src/elf-compat.h   |  124 ++++++++
 src/elf.c          |  827 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/elf32.c        |   43 +++
 src/elf64.c        |   44 +++
 src/gencore.h      |    1 
 src/proc.c         |  278 +++++++++++++++++
 24 files changed, 2667 insertions(+)
 create mode 100644 COPYING
 create mode 100644 COPYING.LIBGENCORE
 create mode 100644 Changelog
 create mode 100644 README
 create mode 100644 doc/Makefile.am
 create mode 100644 doc/gencore.1
 create mode 100644 doc/gencore.3
 create mode 100644 gencore.service
 create mode 100644 gencore.socket
 create mode 100644 gencore.spec.in
 create mode 100644 [email protected]
 create mode 100644 libgencore.pc.in
 create mode 100644 src/Makefile.am
 create mode 100644 src/client.c
 create mode 100644 src/coredump.c
 create mode 100644 src/coredump.h
 create mode 100644 src/elf-compat.h
 create mode 100644 src/elf.c
 create mode 100644 src/elf32.c
 create mode 100644 src/elf64.c
 create mode 100644 src/gencore.h
 create mode 100644 src/proc.c
Signature · 1 line
Janani
#1 lore raw

PATCH 1/33 Configure and Make files

configure.ac and automake files for the repository.

---
 Makefile.am  |    2 ++
 configure.ac |    7 +++++++
 2 files changed, 9 insertions(+)
Makefile.am +2 -0
index e69de29..2cc6dc7 100644
@@ -0,0 +1,2 @@
+AUTOMAKE_OPTIONS = foreign
+SUBDIRS =
configure.ac +7 -0
index e69de29..f34a0c3 100644
@@ -0,0 +1,7 @@
+AC_INIT([Gencore], [0.1], [[email protected]], [gencore], [])
+LT_INIT()
+AM_INIT_AUTOMAKE([-Wall -Werror foreign])
+AC_CONFIG_HEADERS([config.h])
+AC_PROG_CC
+AC_CONFIG_FILES(Makefile)
+AC_OUTPUT()
#2 lore raw

PATCH 2/33 Validity of arguments

In reply to Janani Venkataraman

The validity of the arguments passed to the program are checked. PID is a mandatory argument. The core file name is optional, if not specified the default file name is core.pid.

If a self dump is requested then, the application is called with a --daemon option or the --socket option.

If the SYSTEMD_SOCKET_OPTION is enabled, then the socket option will be fully functional. The system where the program is run needs to have sd-daemon.h. For this we need to have the correct version of systemd which has this socket option enabled.

---
 Makefile.am     |    2 +
 configure.ac    |    2 +
 src/Makefile.am |    7 ++++
 src/coredump.c  |  104 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 113 insertions(+), 2 deletions(-)
 create mode 100644 src/Makefile.am
 create mode 100644 src/coredump.c
Makefile.am +1 -1
index 2cc6dc7..dfa49b2 100644
@@ -1,2 +1,2 @@
 AUTOMAKE_OPTIONS = foreign
-SUBDIRS =
+SUBDIRS = src
configure.ac +1 -1
index f34a0c3..8c785cd 100644
@@ -4,4 +4,4 @@ AM_INIT_AUTOMAKE([-Wall -Werror foreign])
 AC_CONFIG_HEADERS([config.h])
 AC_PROG_CC
 AC_CONFIG_FILES(Makefile)
-AC_OUTPUT()
+AC_OUTPUT(src/Makefile)
src/Makefile.am new file +7 -0
new file mode 100644
index 0000000..f7b25fa
@@ -0,0 +1,7 @@
+AUTOMAKE_OPTIONS = foreign
+HAVE_SYSTEMD_SOCKET_SUPPORT = 0
+CFLAGS += -I. -DHAVE_SYSTEMD_SOCKET_SUPPORT='$(HAVE_SYSTEMD_SOCKET_SUPPORT)'
+
+bin_PROGRAMS = gencore
+gencore_SOURCES = coredump.c
+
src/coredump.c new file +104 -0

104 lines not on this page · raw message

#3 lore raw

PATCH 3/33 Process Status

In reply to Janani Venkataraman

Fetch the process status from /proc/pid/stat as it will be required for the dump.

---
 src/Makefile.am |    2 +
 src/coredump.c  |    4 +++
 src/coredump.h  |   13 ++++++++
 src/proc.c      |   86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 104 insertions(+), 1 deletion(-)
 create mode 100644 src/coredump.h
 create mode 100644 src/proc.c
src/Makefile.am +1 -1
index f7b25fa..9e5d3c0 100644
@@ -3,5 +3,5 @@ HAVE_SYSTEMD_SOCKET_SUPPORT = 0
 CFLAGS += -I. -DHAVE_SYSTEMD_SOCKET_SUPPORT='$(HAVE_SYSTEMD_SOCKET_SUPPORT)'
 
 bin_PROGRAMS = gencore
-gencore_SOURCES = coredump.c
+gencore_SOURCES = coredump.c proc.c
 
src/coredump.c +4 -0
index 7a7fe11..ad9ee7d 100644
@@ -26,10 +26,14 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <coredump.h>
 
 /* For logging all the messages */
 FILE *fp_log;
 
+/* Status of the dump */
+int status;
+
 /* Logging messages */
 void gencore_log(char *fmt, ...)
 {
src/coredump.h new file +13 -0
new file mode 100644
index 0000000..cc77197
@@ -0,0 +1,13 @@
+#define COMM_LEN 17            /* Maximum length of command line */
+#define NUM_STAT_FEILDS 30     /* Number of fields read from /proc/pid/stat */
+
+/* Status of the dump */
+extern int status;
+
+/* Structure for Status of process */
+struct pid_stat {
+	int ps_pid;
+	char ps_comm[COMM_LEN];
+	char ps_state;
+	unsigned long long ps_num[NUM_STAT_FEILDS];
+};
src/proc.c new file +86 -0

86 lines not on this page · raw message

#4 lore raw

PATCH 4/33 Hold threads

In reply to Janani Venkataraman

Getting number of threads and their respective IDs through /proc/pid/stat and /proc/pid/task.

The threads are then seized and interrupted. After the dump is taken they are detached.

---
 src/coredump.c |  149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/coredump.h |   10 ++++
 src/proc.c     |   52 ++++++++++++++++++++
 3 files changed, 210 insertions(+), 1 deletion(-)
src/coredump.c +148 -1
index ad9ee7d..dd9089f 100644
@@ -26,6 +26,9 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/ptrace.h>
 #include <coredump.h>
 
 /* For logging all the messages */
@@ -43,10 +46,154 @@ void gencore_log(char *fmt, ...)
 	va_end(argptr);
 }
 
+/* Core process object */
+struct core_proc cp;
+
+/* Initialised core process members */
+void init_core(void)
+{
+	memset(&cp, 0, sizeof(struct core_proc));
+}
+
+/* Gets the Thread IDS and siezes them */
+int seize_threads(int pid)
+{
+	char filename[40];
+	DIR *dir;
+	int ct = 0, ret = 0, tmp_tid;
+	struct dirent *entry;
+	char state;
+
+	ret = get_thread_count(pid);
+	if (ret == -1)
+		return -1;
+
+	cp.thread_count = ret;
+	cp.t_id = calloc(cp.thread_count, sizeof(int));
+	if (!cp.t_id) {
+		status = errno;
+		gencore_log("Could not allocate memory for thread_ids.\n");
+		return -1;
+	}
+
+	snprintf(filename, 40, "/proc/%d/task", pid);
+	dir = opendir(filename);
+
+	while ((entry = readdir(dir))) {
+		if (entry->d_type == DT_DIR && entry->d_name[0] != '.') {
+			tmp_tid = atoi(entry->d_name);
+			ret = ptrace(PTRACE_SEIZE, tmp_tid, 0, 0);
+			if (ret) {
+				state = get_thread_status(tmp_tid);
+				if (state == 'Z')
+					goto assign;
+				status = errno;
+				gencore_log("Could not seize thread: %d\n",
+								tmp_tid);
+				break;
+			}
+			ret = ptrace(PTRACE_INTERRUPT, tmp_tid, 0, 0);
+			if (ret) {
+				state = get_thread_status(tmp_tid);
+				if (state == 'Z')
+					goto assign;
+				status = errno;
+				gencore_log("Could not interrupt thread: %d\n",
+								tmp_tid);
+				break;
+			}
+assign:
+			/* If a new thread, is created after we fetch the thread_count,
+			 * we may encounter a buffer overflow situation in the cp_tid.
+			 * Hence we check this case and re-allocate memory if required.
+			 */
+			cp.t_id[ct++] = tmp_tid;
+		}
+	}
+
+	/* Reassigning based on successful seizes */
+	cp.thread_count = ct;
+
+	closedir(dir);
+
+	/* Successful seize and interrupt on all threads makes ret = 0 */
+	return ret;
+}
+
+/* Wait for threads to stop */
+int wait_for_threads_to_stop(void)
+{
+	int i;
+	char state;
+
+	/*
+	 * We check for the process to stop infinitely now. We need
+	 * to break out after some definite time. Need to work on
+	 * that.
+	 */
+	for (i = 0; i < cp.thread_count; i++) {
+		do {
+			state = get_thread_status(cp.t_id[i]);
+			if (state != 't')
+				sched_yield();
+		} while (state != 't' && state!='Z' && state != -1);
+		if (state == -1)
+			return -1;
+	}
+
+	return 0;
+}
+
+/* Release the threads that are held */
+int release_threads(void)
+{
+	int i, ret = 0;
+	char state;
+
+	/* Detach the process to be dumped */
+	for (i = 0; i < cp.thread_count; i++) {
+		state = get_thread_status(cp.t_id[i]);
+		if (state == 't') {
+			ret += ptrace(PTRACE_DETACH, cp.t_id[i], 0, 0);
+			if (ret)
+				gencore_log("Could not detach from thread: %d\n",
+								cp.t_id[i]);
+		}
+	}
+
+	/* Successful detach on all threads makes ret = 0 */
+	return ret;
+}
+
 /* Performs the core dump */
 int do_coredump(int pid, char *core_file)
 {
-	return 0;
+	int ret;
+
+	/* Initialise members of core process */
+	init_core();
+
+	/* Getting thread information and seizing them */
+	ret = seize_threads(pid);
+	if (ret)
+		goto cleanup;
+
+	/* Wait for threads to stop */
+	ret = wait_for_threads_to_stop();
+	if (ret)
+		goto cleanup;
+
+cleanup:
+
+	/* Release the threads */
+	release_threads();
+
+	if (cp.t_id)
+		free(cp.t_id);
+
+	errno = status;
+
+	return ret;
 }
 
 /* Daemon for self dump */
src/coredump.h +10 -0
index cc77197..00cb008 100644
@@ -1,6 +1,10 @@
 #define COMM_LEN 17            /* Maximum length of command line */
 #define NUM_STAT_FEILDS 30     /* Number of fields read from /proc/pid/stat */
 
+#define THREAD_COUNT_IDX 16	/* Index for number of threads */
+
+#define __ps_thread_count ps_num[THREAD_COUNT_IDX]	/* Process Information */
+
 /* Status of the dump */
 extern int status;
 
@@ -11,3 +15,9 @@ struct pid_stat {
 	char ps_state;
 	unsigned long long ps_num[NUM_STAT_FEILDS];
 };
+
+/* Structure for the Core of the Process */
+struct core_proc {
+	int thread_count;		/* Number of threads */
+	int *t_id;			/* Threads_ids of all the threads */
+};
src/proc.c +52 -0

61 lines not on this page · raw message

#5 lore raw

Re: [PATCH 04/33] Hold threads

Quoted 24 lines · Janani Venkataraman

Getting number of threads and their respective IDs through /proc/pid/stat and /proc/pid/task.

The threads are then seized and interrupted. After the dump is taken they are detached.

---

src/coredump.c +15 -0
+/* Gets the Thread IDS and siezes them */
+int seize_threads(int pid)
+{
+	char filename[40];
+	DIR *dir;
+	int ct = 0, ret = 0, tmp_tid;
+	struct dirent *entry;
+	char state;
+
+	ret = get_thread_count(pid);
+	if (ret == -1)
+		return -1;
+
+	cp.thread_count = ret;
+	cp.t_id = calloc(cp.thread_count, sizeof(int));

src/coredump.c void gencore_log(char *fmt, ...) · line 82

Quoted 10 lines
src/coredump.c +10 -0
+	if (!cp.t_id) {
+		status = errno;
+		gencore_log("Could not allocate memory for thread_ids.\n");
+		return -1;
+	}
+
+	snprintf(filename, 40, "/proc/%d/task", pid);
+	dir = opendir(filename);
+
+	while ((entry = readdir(dir))) {

This simple loop is not enough -- threads may appear and disappear while you do the readdir and seize, so you should scan it several times to make sure you caught all the threads.

You can look at how this is done in CRIU in cr-dump,c:collect_threads().

Quoted 39 lines
src/coredump.c +39 -0
+		if (entry->d_type == DT_DIR && entry->d_name[0] != '.') {
+			tmp_tid = atoi(entry->d_name);
+			ret = ptrace(PTRACE_SEIZE, tmp_tid, 0, 0);
+			if (ret) {
+				state = get_thread_status(tmp_tid);
+				if (state == 'Z')
+					goto assign;
+				status = errno;
+				gencore_log("Could not seize thread: %d\n",
+								tmp_tid);
+				break;
+			}
+			ret = ptrace(PTRACE_INTERRUPT, tmp_tid, 0, 0);
+			if (ret) {
+				state = get_thread_status(tmp_tid);
+				if (state == 'Z')
+					goto assign;
+				status = errno;
+				gencore_log("Could not interrupt thread: %d\n",
+								tmp_tid);
+				break;
+			}
+assign:
+			/* If a new thread, is created after we fetch the thread_count,
+			 * we may encounter a buffer overflow situation in the cp_tid.
+			 * Hence we check this case and re-allocate memory if required.
+			 */
+			cp.t_id[ct++] = tmp_tid;
+		}
+	}
+
+	/* Reassigning based on successful seizes */
+	cp.thread_count = ct;
+
+	closedir(dir);
+
+	/* Successful seize and interrupt on all threads makes ret = 0 */
+	return ret;
+}
#6 lore raw

Re: [PATCH 04/33] Hold threads

Quoted 29 lines · Pavel Emelyanov
Quoted 28 lines · Janani Venkataraman

Getting number of threads and their respective IDs through /proc/pid/stat and /proc/pid/task.

The threads are then seized and interrupted. After the dump is taken they are detached.

---

src/coredump.c +20 -0
+/* Gets the Thread IDS and siezes them */
+int seize_threads(int pid)
+{
+	char filename[40];
+	DIR *dir;
+	int ct = 0, ret = 0, tmp_tid;
+	struct dirent *entry;
+	char state;
+
+	ret = get_thread_count(pid);
+	if (ret == -1)
+		return -1;
+
+	cp.thread_count = ret;
+	cp.t_id = calloc(cp.thread_count, sizeof(int));
+	if (!cp.t_id) {
+		status = errno;
+		gencore_log("Could not allocate memory for thread_ids.\n");
+		return -1;
+	}
Quoted 8 lines

src/coredump.c void gencore_log(char *fmt, ...) · line 82

Quoted 5 lines
src/coredump.c +5 -0
+
+	snprintf(filename, 40, "/proc/%d/task", pid);
+	dir = opendir(filename);
+
+	while ((entry = readdir(dir))) {

This simple loop is not enough -- threads may appear and disappear while you do the readdir and seize, so you should scan it several times to make sure you caught all the threads.

I will look into this.

Quoted 1 line

You can look at how this is done in CRIU in cr-dump,c:collect_threads().

Yes I will look into that function. Thanks.

Thanks.
Janani

Quoted 39 lines
Quoted 39 lines
src/coredump.c +39 -0
+		if (entry->d_type == DT_DIR && entry->d_name[0] != '.') {
+			tmp_tid = atoi(entry->d_name);
+			ret = ptrace(PTRACE_SEIZE, tmp_tid, 0, 0);
+			if (ret) {
+				state = get_thread_status(tmp_tid);
+				if (state == 'Z')
+					goto assign;
+				status = errno;
+				gencore_log("Could not seize thread: %d\n",
+								tmp_tid);
+				break;
+			}
+			ret = ptrace(PTRACE_INTERRUPT, tmp_tid, 0, 0);
+			if (ret) {
+				state = get_thread_status(tmp_tid);
+				if (state == 'Z')
+					goto assign;
+				status = errno;
+				gencore_log("Could not interrupt thread: %d\n",
+								tmp_tid);
+				break;
+			}
+assign:
+			/* If a new thread, is created after we fetch the thread_count,
+			 * we may encounter a buffer overflow situation in the cp_tid.
+			 * Hence we check this case and re-allocate memory if required.
+			 */
+			cp.t_id[ct++] = tmp_tid;
+		}
+	}
+
+	/* Reassigning based on successful seizes */
+	cp.thread_count = ct;
+
+	closedir(dir);
+
+	/* Successful seize and interrupt on all threads makes ret = 0 */
+	return ret;
+}
#7 lore raw

Re: [PATCH 04/33] Hold threads

In reply to Pavel Emelyanov
Quoted 38 lines · Pavel Emelyanov
Quoted 23 lines · Janani Venkataraman

Getting number of threads and their respective IDs through /proc/pid/stat and /proc/pid/task.

The threads are then seized and interrupted. After the dump is taken they are detached.

---

(quoted diff) +15 -0
+/* Gets the Thread IDS and siezes them */
+int seize_threads(int pid)
+{
+	char filename[40];
+	DIR *dir;
+	int ct = 0, ret = 0, tmp_tid;
+	struct dirent *entry;
+	char state;
+
+	ret = get_thread_count(pid);
+	if (ret == -1)
+		return -1;
+
+	cp.thread_count = ret;
+	cp.t_id = calloc(cp.thread_count, sizeof(int));

Review

Quoted 10 lines
(quoted diff) +10 -0
+	if (!cp.t_id) {
+		status = errno;
+		gencore_log("Could not allocate memory for thread_ids.\n");
+		return -1;
+	}
+
+	snprintf(filename, 40, "/proc/%d/task", pid);
+	dir = opendir(filename);
+
+	while ((entry = readdir(dir))) {

This simple loop is not enough -- threads may appear and disappear while you do the readdir and seize, so you should scan it several times to make sure you caught all the threads.

Quoted 2 lines

You can look at how this is done in CRIU in cr-dump,c:collect_threads().

If there are any new threads generated after the process is issued a gencore, we need to ensure even those threads are captured and seized. Hence we read the /proc/pid/task, till no new threads are created.

We do a readdir again and again, till we find no new threads. If a new thread is found, it is inserted in the right position. This process is repeated till readdir reads no new threads.

---
 src/coredump.c |  161 +++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 117 insertions(+), 44 deletions(-)
src/coredump.c +117 -44
index 49af7b3..2247ba2 100644
@@ -93,68 +93,141 @@ void init_core(void)
 	memset(&cp, 0, sizeof(struct core_proc));
 }
 
-/* Gets the Thread IDS and siezes them */
-int seize_threads(int pid)
+/* Check if a particular TID is already added to cp.t_id */
+int search(int pid, int n)
 {
-	char filename[40];
-	DIR *dir;
-	int ct = 0, ret = 0, tmp_tid;
-	struct dirent *entry;
-	char state;
+	int first, last, middle;
+
+	first = 0;
+	last = n - 1;
+
+	while( first <= last ) {
+		
+		middle = (first + last)/2;
+
+		if ( cp.t_id[middle] < pid )
+			first = middle + 1;
+		else if ( cp.t_id[middle] == pid )
+			return 1;
+		else
+			last = middle - 1;
+	}
 
-	ret = get_thread_count(pid);
-	if (ret == -1)
-		return -1;
+	return 0;
+}
 
-	cp.thread_count = ret;
-	cp.t_id = calloc(cp.thread_count, sizeof(int));
-	if (!cp.t_id) {
-		status = errno;
-		gencore_log("Could not allocate memory for thread_ids.\n");
-		return -1;
+/* Insert a TID to cp.t_id */
+void insert (int tid, int n)
+{
+	int i, j, flag = 0;
+
+	for (i = 0; (i < n) && (tid > cp.t_id[i]); i++);
+
+	j = i;
+	while (j < n) {
+		cp.t_id[j + 1] = cp.t_id[j];
+		j++;
 	}
 
+	cp.t_id[i] = tid;
+}
+
+/*
+ * Read the directory /proc/pid/task again and again
+ * till we find no new threads.
+ */
+int scan_threads(int pid)
+{
+	DIR *dir;
+	struct dirent *entry;
+	int tmp_tid, ret = 0, k = 0;
+	char state;
+	char filename[40];
+
 	snprintf(filename, 40, "/proc/%d/task", pid);
+	
 	dir = opendir(filename);
 
 	while ((entry = readdir(dir))) {
 		if (entry->d_type == DT_DIR && entry->d_name[0] != '.') {
 			tmp_tid = atoi(entry->d_name);
-			ret = ptrace(PTRACE_SEIZE, tmp_tid, 0, 0);
-			if (ret) {
-				state = get_thread_status(tmp_tid);
-				if (state == 'Z')
-					goto assign;
-				status = errno;
-				gencore_log("Could not seize thread: %d\n",
-								tmp_tid);
-				break;
-			}
-			ret = ptrace(PTRACE_INTERRUPT, tmp_tid, 0, 0);
-			if (ret) {
-				state = get_thread_status(tmp_tid);
-				if (state == 'Z')
-					goto assign;
-				status = errno;
-				gencore_log("Could not interrupt thread: %d\n",
-								tmp_tid);
-				break;
-			}
-assign:
-			/* If a new thread, is created after we fetch the thread_count,
-			 * we may encounter a buffer overflow situation in the cp_tid.
-			 * Hence we check this case and re-allocate memory if required.
+			/*
+			 * Search for the thread, if not present, seize, interrupt
+			 * and insert it in cp.t_id.
 			 */
-			cp.t_id[ct++] = tmp_tid;
+			if (!search(tmp_tid, cp.thread_count)) {
+
+				k++;
+
+				ret = ptrace(PTRACE_SEIZE, tmp_tid, 0, 0);
+				if (ret) {
+					state = get_thread_status(tmp_tid);
+					if (state == 'Z')
+						goto assign;
+					status = errno;
+					gencore_log("Could not seize thread: %d\n",
+							tmp_tid);
+					break;
+				}
+
+				ret = ptrace(PTRACE_INTERRUPT, tmp_tid, 0, 0);
+				if (ret) {
+					state = get_thread_status(tmp_tid);
+					if (state == 'Z')
+						goto assign;
+					status = errno;
+					gencore_log("Could not interrupt thread: %d\n",
+							tmp_tid);
+					break;
+				}
+assign:
+				cp.t_id = (char *) realloc(cp.t_id, (cp.thread_count + 1) * sizeof(int));
+				if (!cp.t_id) {
+					status = errno;
+					gencore_log("Could not allocate memory for thread_ids.\n");
+					return -1;
+				}
+
+				insert(tmp_tid, cp.thread_count);
+				cp.thread_count ++;
+			}
 		}
 	}
 
-	/* Reassigning based on successful seizes */
-	cp.thread_count = ct;
-
 	closedir(dir);
 
+	if (!k)
+		return 1;
+
+	return ret;
+}
+
+/* Gets the Thread IDS and siezes them */
+int seize_threads(int pid)
+{
+	int ret = 0;
+
+	cp.thread_count = 0;
+	cp.t_id = calloc(cp.thread_count, sizeof(int));
+	if (!cp.t_id) {
+		status = errno;
+		gencore_log("Could not allocate memory for thread_ids.\n");
+		return -1;
+	}
+
+	/*
+	 * Read the directory /proc/pid/task again and again
+	 * till we find no new threads.
+	 */
+
+	do {
+		ret = scan_threads(pid);
+	} while (ret == 0);
+
 	/* Successful seize and interrupt on all threads makes ret = 0 */
+	if (ret == 1)
+		return 0;
+
 	return ret;
 }
 

Thanks.
Janani

#8 lore raw

PATCH 5/33 Fetching Memory maps

In reply to Janani Venkataraman

Fetching virtual memory areas through /proc/pid/maps and storing it in a linked list in order to populate the program headers and file maps.

---
 src/coredump.c |    8 +++++
 src/coredump.h |   11 ++++++
 src/proc.c     |   95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 114 insertions(+)
src/coredump.c +8 -0
index dd9089f..7a559e2 100644
@@ -183,6 +183,11 @@ int do_coredump(int pid, char *core_file)
 	if (ret)
 		goto cleanup;
 
+	/* Get VMAS */
+	ret = get_vmas(pid, &cp);
+	if (ret)
+		goto cleanup;
+
 cleanup:
 
 	/* Release the threads */
@@ -191,6 +196,9 @@ cleanup:
 	if (cp.t_id)
 		free(cp.t_id);
 
+	if (cp.vmas)
+		free_maps(cp.vmas);
+
 	errno = status;
 
 	return ret;
src/coredump.h +11 -0
index 00cb008..291e13b 100644
@@ -16,8 +16,19 @@ struct pid_stat {
 	unsigned long long ps_num[NUM_STAT_FEILDS];
 };
 
+/* Structure for maps */
+struct maps {
+	unsigned long long src, dst, offset;
+	char r, w, x;
+	long inode;
+	struct maps *next;
+	char fname[0];
+};
+
 /* Structure for the Core of the Process */
 struct core_proc {
 	int thread_count;		/* Number of threads */
 	int *t_id;			/* Threads_ids of all the threads */
+	struct maps *vmas;		/* VMAs */
+	int phdrs_count;		/* Number of Program headers */
 };
src/proc.c +95 -0

104 lines not on this page · raw message

#9 lore raw

PATCH 6/33 Check ELF class

In reply to Janani Venkataraman

The process to be dumped can be either a 32bit application or a 64 bit application. We first need to check this and proceed with the dump accordingly.

---
 src/coredump.c |    5 +++++
 src/coredump.h |    1 +
 src/proc.c     |   45 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 51 insertions(+)
src/coredump.c +5 -0
index 7a559e2..f9c7176 100644
@@ -188,6 +188,11 @@ int do_coredump(int pid, char *core_file)
 	if (ret)
 		goto cleanup;
 
+	/* Compat Support */
+	cp.elf_class = ret = get_elf_class(pid, &cp);
+	if (ret == -1)
+		goto cleanup;
+
 cleanup:
 
 	/* Release the threads */
src/coredump.h +1 -0
index 291e13b..25042f5 100644
@@ -31,4 +31,5 @@ struct core_proc {
 	int *t_id;			/* Threads_ids of all the threads */
 	struct maps *vmas;		/* VMAs */
 	int phdrs_count;		/* Number of Program headers */
+	int elf_class;			/* Elf class of the process */
 };
src/proc.c +45 -0

54 lines not on this page · raw message

#10 lore raw

PATCH 7/33 Do elf_coredump

In reply to Janani Venkataraman

Perform the actual dump depending on whether it is 32 bit or 64 bit.

We create three files elf.c elf32.c and elf64.c. In elf.c we have all the elf related code in a generic format(not 32 or 64 bit specific). In elf32.c and elf64.c we include elf.c and #define the required datatypes and functions into 32 or 64 specific values respectively.

Essentially we have two copies of elf.c, one which is 32 bit specific and the other 64 bit specific and hence the appropriate function is called based on the application to be dumped.

---
 src/Makefile.am |    2 +-
 src/coredump.c  |    9 +++++++++
 src/elf.c       |   31 +++++++++++++++++++++++++++++++
 src/elf32.c     |   33 +++++++++++++++++++++++++++++++++
 src/elf64.c     |   33 +++++++++++++++++++++++++++++++++
 5 files changed, 107 insertions(+), 1 deletion(-)
 create mode 100644 src/elf.c
 create mode 100644 src/elf32.c
 create mode 100644 src/elf64.c
src/Makefile.am +1 -1
index 9e5d3c0..90d8b25 100644
@@ -3,5 +3,5 @@ HAVE_SYSTEMD_SOCKET_SUPPORT = 0
 CFLAGS += -I. -DHAVE_SYSTEMD_SOCKET_SUPPORT='$(HAVE_SYSTEMD_SOCKET_SUPPORT)'
 
 bin_PROGRAMS = gencore
-gencore_SOURCES = coredump.c proc.c
+gencore_SOURCES = coredump.c proc.c elf32.c elf64.c
 
src/coredump.c +9 -0
index f9c7176..b1ee99d 100644
@@ -28,6 +28,7 @@
 #include <stdlib.h>
 #include <dirent.h>
 #include <errno.h>
+#include <elf.h>
 #include <sys/ptrace.h>
 #include <coredump.h>
 
@@ -193,6 +194,14 @@ int do_coredump(int pid, char *core_file)
 	if (ret == -1)
 		goto cleanup;
 
+	/* Do elf_dump */
+	if (cp.elf_class == ELFCLASS32)
+		ret = do_elf32_coredump(pid, &cp);
+	else
+		ret = do_elf64_coredump(pid, &cp);
+	if (ret)
+		goto cleanup;
+
 cleanup:
 
 	/* Release the threads */
src/elf.c new file +31 -0
new file mode 100644
index 0000000..280df13
@@ -0,0 +1,31 @@
+/*
+ * ELF helper routines for gencore
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2013
+ *
+ * Authors:
+ *      Janani Venkataraman <[email protected]>
+ *      Suzuki K. Poulose <[email protected]>
+ */
+
+#include <stdio.h>
+#include "coredump.h"
+
+int do_elf_coredump(int pid, struct core_proc *cp)
+{
+	return 0;
+}
src/elf32.c new file +33 -0
new file mode 100644
index 0000000..d6b40b4
@@ -0,0 +1,33 @@
+/*
+ * ELF helper routines for gencore
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2013
+ *
+ * Authors:
+ *      Janani Venkataraman <[email protected]>
+ */
+
+/*
+ * We include elf.c to add all the elf specific operations here.
+ * In this file, we define all 32 bit specific data and hence
+ * this file would contain all elf 32 bit specific functions
+ * and operations once elf.c is included.
+ */
+
+#define do_elf_coredump do_elf32_coredump
+
+#include "elf.c"
src/elf64.c new file +33 -0
new file mode 100644
index 0000000..d8b5e89
@@ -0,0 +1,33 @@
+/*
+ * ELF helper routines for gencore
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2013
+ *
+ * Authors:
+ *      Janani Venkataraman <[email protected]>
+ */
+
+/*
+ * We include elf.c to add all the elf specific operations here.
+ * In this file, we define all 64 bit specific data and hence
+ * this file would contain all elf 64 bit specific functions
+ * and operations once elf.c is included.
+ */
+
+#define do_elf_coredump do_elf64_coredump
+
+#include "elf.c"
#11 lore raw

PATCH 8/33 Fills elf header

In reply to Janani Venkataraman

Populates the ELF header. It fills the magic number, version, machine number of program headers and other members of the ELF_HEADER.

All the information is derived from the ELF_HEADER of the exe of the process to be dumped through /proc/pid/exe.

---
 src/coredump.c |    3 ++
 src/coredump.h |    1 +
 src/elf.c      |   96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/elf32.c    |    4 ++
 src/elf64.c    |    4 ++
 5 files changed, 108 insertions(+)
src/coredump.c +3 -0
index b1ee99d..de0a7ce 100644
@@ -213,6 +213,9 @@ cleanup:
 	if (cp.vmas)
 		free_maps(cp.vmas);
 
+	if (cp.elf_hdr)
+		free(cp.elf_hdr);
+
 	errno = status;
 
 	return ret;
src/coredump.h +1 -0
index 25042f5..4e508c1 100644
@@ -32,4 +32,5 @@ struct core_proc {
 	struct maps *vmas;		/* VMAs */
 	int phdrs_count;		/* Number of Program headers */
 	int elf_class;			/* Elf class of the process */
+	void *elf_hdr;			/* Stores the ELF_header */
 };
src/elf.c +96 -0

106 lines not on this page · raw message

src/elf32.c +4 -0
index d6b40b4..1a95ff2 100644
@@ -30,4 +30,8 @@
 
 #define do_elf_coredump do_elf32_coredump
 
+#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Phdr Elf32_Phdr
+#define Elf_Shdr Elf32_Shdr
+
 #include "elf.c"
src/elf64.c +4 -0
index d8b5e89..953b826 100644
@@ -30,4 +30,8 @@
 
 #define do_elf_coredump do_elf64_coredump
 
+#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Phdr Elf64_Phdr
+#define Elf_Shdr Elf64_Shdr
+
 #include "elf.c"
#12 lore raw

PATCH 9/33 Adding notes infrastructure

In reply to Janani Venkataraman

Creating Elf_note structure and adding the required functions to add the notes.

---
 src/coredump.c |   15 ++++++++++++
 src/coredump.h |   13 ++++++++++
 src/elf.c      |   73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/elf32.c    |    1 +
 src/elf64.c    |    1 +
 5 files changed, 103 insertions(+)
src/coredump.c +15 -0
index de0a7ce..0fd6343e 100644
@@ -166,6 +166,18 @@ int release_threads(void)
 	return ret;
 }
 
+/* Free Notes */
+void free_notes(struct mem_note *head)
+{
+	struct mem_note *tmp;
+
+	while (head) {
+		tmp = head->next;
+		free(head);
+		head = tmp;
+	}
+}
+
 /* Performs the core dump */
 int do_coredump(int pid, char *core_file)
 {
@@ -216,6 +228,9 @@ cleanup:
 	if (cp.elf_hdr)
 		free(cp.elf_hdr);
 
+	if (cp.notes)
+		free_notes(cp.notes);
+
 	errno = status;
 
 	return ret;
src/coredump.h +13 -0
index 4e508c1..9b19f19 100644
@@ -25,6 +25,18 @@ struct maps {
 	char fname[0];
 };
 
+/*
+ * Structure for Notes
+ * We follow this linked list data-type as we dont know the number of notes
+ * as that depends on the architecture. Also the notebuf contains the final
+ * in-file format for the note data.
+ */
+struct mem_note {
+	unsigned char *notebuf;		/* Notes - type, name_sz, datasz, name and data */
+	unsigned int size;		/* Size of Note */
+	struct mem_note *next;
+};
+
 /* Structure for the Core of the Process */
 struct core_proc {
 	int thread_count;		/* Number of threads */
@@ -33,4 +45,5 @@ struct core_proc {
 	int phdrs_count;		/* Number of Program headers */
 	int elf_class;			/* Elf class of the process */
 	void *elf_hdr;			/* Stores the ELF_header */
+	struct mem_note *notes;		/* Head of Notes */
 };
src/elf.c +73 -0

79 lines not on this page · raw message

src/elf32.c +1 -0
index 1a95ff2..01b3923 100644
@@ -33,5 +33,6 @@
 #define Elf_Ehdr Elf32_Ehdr
 #define Elf_Phdr Elf32_Phdr
 #define Elf_Shdr Elf32_Shdr
+#define Elf_Nhdr Elf32_Nhdr
 
 #include "elf.c"
src/elf64.c +1 -0
index 953b826..a99a73b 100644
@@ -33,5 +33,6 @@
 #define Elf_Ehdr Elf64_Ehdr
 #define Elf_Phdr Elf64_Phdr
 #define Elf_Shdr Elf64_Shdr
+#define Elf_Nhdr Elf64_Nhdr
 
 #include "elf.c"
#13 lore raw

PATCH 10/33 Populates PRPS info

In reply to Janani Venkataraman

Populates the prps_info by reading /proc/pid/stat and /proc/pid/cmdline.

---
 src/coredump.h   |   10 +++++++++
 src/elf-compat.h |   48 +++++++++++++++++++++++++++++++++++++++++++++
 src/elf.c        |   58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/elf32.c      |    1 +
 src/elf64.c      |    1 +
 5 files changed, 118 insertions(+)
 create mode 100644 src/elf-compat.h
src/coredump.h +10 -0
index 9b19f19..d5562fe 100644
@@ -1,9 +1,19 @@
 #define COMM_LEN 17            /* Maximum length of command line */
 #define NUM_STAT_FEILDS 30     /* Number of fields read from /proc/pid/stat */
 
+#define PPID 0			/* Index for parent process ID */
+#define PGRP 1			/* Index for process group ID */
+#define SID 2			/* Index for session ID */
+#define FLAG 5			/* Index for flags */
+#define NICE 15		/* Index for nice value */
 #define THREAD_COUNT_IDX 16	/* Index for number of threads */
 
 #define __ps_thread_count ps_num[THREAD_COUNT_IDX]	/* Process Information */
+#define __ps_ppid ps_num[PPID]				/* Process PID */	
+#define __ps_pgrp ps_num[PGRP]				/* Process Group ID */ 
+#define __ps_sid ps_num[SID]				/* Process Session ID */
+#define __ps_flag ps_num[FLAG]				/* Process Flags */
+#define __ps_nice ps_num[NICE]				/* Process Nice Value */
 
 /* Status of the dump */
 extern int status;
src/elf-compat.h new file +48 -0

48 lines not on this page · raw message

src/elf.c +58 -0

76 lines not on this page · raw message

src/elf32.c +1 -0
index 01b3923..8778bed 100644
@@ -34,5 +34,6 @@
 #define Elf_Phdr Elf32_Phdr
 #define Elf_Shdr Elf32_Shdr
 #define Elf_Nhdr Elf32_Nhdr
+#define Elf_prpsinfo compat_elf_prpsinfo
 
 #include "elf.c"
src/elf64.c +1 -0
index a99a73b..d062888 100644
@@ -34,5 +34,6 @@
 #define Elf_Phdr Elf64_Phdr
 #define Elf_Shdr Elf64_Shdr
 #define Elf_Nhdr Elf64_Nhdr
+#define Elf_prpsinfo elf_prpsinfo
 
 #include "elf.c"
#14 lore raw

PATCH 11/33 Populate AUXV

In reply to Janani Venkataraman

Populate the auxillary vector using /proc/pid/auxv.

---
 src/elf.c |   73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)
src/elf.c +73 -0
index 18bbeeb..b7fd6d0 100644
@@ -26,12 +26,16 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
 #include <sys/uio.h>
 #include <sys/procfs.h>
 #include <linux/elf.h>
 #include "elf-compat.h"
 #include "coredump.h"
 
+#define PAGESIZE getpagesize()
+
 #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
 
 /* Appending the note to the list */
@@ -240,6 +244,70 @@ static int get_prpsinfo(int pid, struct core_proc *cp)
 	return add_note("CORE", NT_PRPSINFO, sizeof(prps), &prps, cp);
 }
 
+/* Populate auxillary vector */
+static int get_auxv(int pid, struct core_proc *cp)
+{
+	unsigned char buff[PAGESIZE];
+	char filename[40];
+	int ret, fd, pages;
+	unsigned char *ptr, *auxv;
+	unsigned int auxv_size;
+
+	snprintf(filename, 40, "/proc/%d/auxv", pid);
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		status = errno;
+		gencore_log("Failure while fetching auxv data from %s.\n",
+								filename);
+		return -1;
+	}
+
+	auxv = malloc(PAGESIZE);
+	if (!auxv) {
+		status = errno;
+		gencore_log("Could not allocate memory for the auxv_buffer.\n");
+		close(fd);
+		return -1;
+	}
+	/* Position to copy the auxv data */
+	ptr = auxv;
+	pages = 1;
+	/*
+	 * We read the auxv data page by page and also we don't not
+	 * know the size of auxv, hence we read till ret becomes
+	 * lesser than PAGESIZE.
+	 */
+	while ((ret = read(fd, buff, PAGESIZE)) > 0) {
+		memcpy(ptr, buff, ret);
+		if (ret < PAGESIZE)   /* Finished reading */
+			break;
+		else {
+			/* We have more data to read */
+			pages++;
+			auxv = realloc(auxv, pages * PAGESIZE);
+			ptr = auxv + ((pages - 1) * PAGESIZE);
+		}
+	}
+	if (ret >= 0)
+		auxv_size = ((pages - 1) * PAGESIZE) + ret;
+	else {
+		status = errno;
+		gencore_log("Failure while fetching auxv data from %s.\n", filename);
+		close(fd);
+		free(auxv);
+		return -1;
+	}
+
+	/* Adding AUXV */
+	ret = add_note("CORE", NT_AUXV, auxv_size, auxv, cp);
+
+	close(fd);
+	free(auxv);
+
+	return ret;
+}
+
 int do_elf_coredump(int pid, struct core_proc *cp)
 {
 	int ret;
@@ -254,5 +322,10 @@ int do_elf_coredump(int pid, struct core_proc *cp)
 	if (ret)
 		return -1;
 
+	/* Get Auxillary Vector */
+	ret = get_auxv(pid, cp);
+	if (ret)
+		return -1;
+
 	return 0;
 }
#15 lore raw

PATCH 12/33 Fetch File maps

In reply to Janani Venkataraman

The file maps details are got from the maps linked list which was populated in Patch 5 and they are filled as a note in the following format.

long count -- how many files are mapped
long page_size -- units for file_ofs
array of [COUNT] elements of
long start
long end
long file_ofs
followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL...

---
 src/elf.c   |   78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/elf32.c |    1 +
 src/elf64.c |    1 +
 3 files changed, 80 insertions(+)
src/elf.c +78 -0
index b7fd6d0..9e3623f 100644
@@ -308,6 +308,79 @@ static int get_auxv(int pid, struct core_proc *cp)
 	return ret;
 }
 
+/*
+ * Get File Maps in the following format:
+ * long count     -- how many files are mapped
+ * long page_size -- units for file_ofs
+ * array of [COUNT] elements of
+ * long start
+ * long end
+ * long file_ofs
+ * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL...
+ */
+static int get_file_maps(struct core_proc *cp)
+{
+	Elf_Long count = 0;
+	Elf_Long fmap_size = 0, namesz = 0;
+	Elf_Long *fmap;
+	Elf_Long *data_pos;
+	unsigned char *name_pos;
+	int ret;
+	struct maps *map = cp->vmas;
+
+	/*
+	 * Finding the actual size of the file map for which we need
+	 * to know number of VMAS(non zero inode) plus the file name size
+	 * for each of the VMAS(non zero inode.
+	 */
+	while (map) {
+		if (map->inode) {
+			count++;
+			namesz += strlen(map->fname) + 1;
+		}
+		map = map->next;
+	}
+
+	/*
+	 * We add 2 for the count and page_size, 3 * (number of vmas)
+	 * for the start, end and file_ofs and finally the entire size
+	 * of the filenames of all the VMAS.
+	 */
+	fmap_size = ((2 + (3 * count)) * sizeof(Elf_Long))
+				+ (namesz * sizeof(unsigned char));
+
+	fmap = calloc(fmap_size, 1);
+	if (!fmap) {
+		status = errno;
+		gencore_log("Could not allocate memory for file map data\n");
+		return -1;
+	}
+
+	data_pos = fmap + 2;
+	name_pos = (unsigned char *)(fmap + 2 + (count * 3));
+
+	map = cp->vmas;
+	while (map) {
+		if (map->inode) {
+			*data_pos++ = map->src;
+			*data_pos++ = map->dst;
+			*data_pos++ = map->offset;
+
+			strcpy(name_pos, map->fname);
+			name_pos += strlen(map->fname) + 1;
+		}
+		map = map->next;
+	}
+
+	fmap[0] = count;
+	fmap[1] = 1;
+
+	ret = add_note("CORE", NT_FILE, fmap_size, fmap, cp);
+	free(fmap);
+
+	return ret;
+}
+
 int do_elf_coredump(int pid, struct core_proc *cp)
 {
 	int ret;
@@ -327,5 +400,10 @@ int do_elf_coredump(int pid, struct core_proc *cp)
 	if (ret)
 		return -1;
 
+	/* Get File maps */
+	ret = get_file_maps(cp);
+	if (ret)
+		return -1;
+
 	return 0;
 }
src/elf32.c +1 -0
index 8778bed..0f7c03d 100644
@@ -35,5 +35,6 @@
 #define Elf_Shdr Elf32_Shdr
 #define Elf_Nhdr Elf32_Nhdr
 #define Elf_prpsinfo compat_elf_prpsinfo
+#define Elf_Long int
 
 #include "elf.c"
src/elf64.c +1 -0
index d062888..77da222 100644
@@ -35,5 +35,6 @@
 #define Elf_Shdr Elf64_Shdr
 #define Elf_Nhdr Elf64_Nhdr
 #define Elf_prpsinfo elf_prpsinfo
+#define Elf_Long long
 
 #include "elf.c"
#16 lore raw

PATCH 13/33 Fetching thread specific Notes

In reply to Janani Venkataraman

Populates prstatus for each and every thread of the task to be dumped by accessing their respective /proc/tid/stat.

Fetching the required registers based on the architecture. The architectures that are handled are X86, S390 and PPC.

We fetch the registers in thread-wise order, as some architectures depend on the ordering of the notes. (e.g, s390)

---
 src/coredump.h   |   12 +++
 src/elf-compat.h |   76 +++++++++++++++++++++
 src/elf.c        |  194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/elf32.c      |    1 
 src/elf64.c      |    1 
 5 files changed, 283 insertions(+), 1 deletion(-)
src/coredump.h +12 -0
index d5562fe..dce6292 100644
@@ -5,15 +5,27 @@
 #define PGRP 1			/* Index for process group ID */
 #define SID 2			/* Index for session ID */
 #define FLAG 5			/* Index for flags */
+#define UTIME 10		/* Index for scheduled user mode */
+#define STIME 11		/* Index for scheduled user mode */
+#define CUTIME 12		/* Index for scheduled user mode time for a process's waited-for children */
+#define CSTIME 13		/* Index for scheduled kernel mode time for a process's waited-for children */
 #define NICE 15		/* Index for nice value */
 #define THREAD_COUNT_IDX 16	/* Index for number of threads */
+#define SIGPEND 27		/* Index for pending signals for a process */
+#define SIGHOLD 29		/* Index for ignored signals for a process */
 
 #define __ps_thread_count ps_num[THREAD_COUNT_IDX]	/* Process Information */
 #define __ps_ppid ps_num[PPID]				/* Process PID */	
 #define __ps_pgrp ps_num[PGRP]				/* Process Group ID */ 
 #define __ps_sid ps_num[SID]				/* Process Session ID */
 #define __ps_flag ps_num[FLAG]				/* Process Flags */
+#define __ps_utime ps_num[UTIME]			/* Process scheduled user mode */
+#define __ps_stime ps_num[STIME]			/* Process scheduled user mode */
+#define __ps_cutime ps_num[CUTIME]			/* Process scheduled user mode time for a process's waited-for children */
+#define __ps_cstime ps_num[CSTIME]			/* Process scheduled kernel mode time for a process's waited-for children */
 #define __ps_nice ps_num[NICE]				/* Process Nice Value */
+#define __ps_sigpend ps_num[SIGPEND]			/* Process pending signals */
+#define __ps_sighold ps_num[SIGHOLD]			/* Process ignored signals */
 
 /* Status of the dump */
 extern int status;
src/elf-compat.h +76 -0

85 lines not on this page · raw message

src/elf.c +193 -1

213 lines not on this page · raw message

src/elf32.c +1 -0
index 0f7c03d..8ec287b 100644
@@ -36,5 +36,6 @@
 #define Elf_Nhdr Elf32_Nhdr
 #define Elf_prpsinfo compat_elf_prpsinfo
 #define Elf_Long int
+#define Elf_prstatus compat_elf_prstatus
 
 #include "elf.c"
src/elf64.c +1 -0
index 77da222..780e2d4 100644
@@ -36,5 +36,6 @@
 #define Elf_Nhdr Elf64_Nhdr
 #define Elf_prpsinfo elf_prpsinfo
 #define Elf_Long long
+#define Elf_prstatus elf_prstatus
 
 #include "elf.c"
#17 lore raw

PATCH 14/33 Populating Program Headers

In reply to Janani Venkataraman

Populates the program headers using memory maps linked list which was populated in patch 5. The source, size and permissions of the maps are found out and filled respectively.

---
 src/coredump.c |    6 +++
 src/coredump.h |    2 +
 src/elf.c      |  108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/elf32.c    |    2 +
 src/elf64.c    |    1 +
 5 files changed, 119 insertions(+)
src/coredump.c +6 -0
index 0fd6343e..5cccb7a 100644
@@ -231,6 +231,12 @@ cleanup:
 	if (cp.notes)
 		free_notes(cp.notes);
 
+	if (cp.phdrs)
+		free(cp.phdrs);
+
+	if (cp.phdrs_count)
+		free(cp.shdrs);
+
 	errno = status;
 
 	return ret;
src/coredump.h +2 -0
index dce6292..a729f16 100644
@@ -68,4 +68,6 @@ struct core_proc {
 	int elf_class;			/* Elf class of the process */
 	void *elf_hdr;			/* Stores the ELF_header */
 	struct mem_note *notes;		/* Head of Notes */
+	void *shdrs;			/* Extra Program Headers */
+	void *phdrs;			/* Program Headers */
 };
src/elf.c +108 -0

131 lines not on this page · raw message

src/elf32.c +2 -0
index 8ec287b..daf9940 100644
@@ -38,4 +38,6 @@
 #define Elf_Long int
 #define Elf_prstatus compat_elf_prstatus
 
+#define Elf_Addr Elf32_Addr
+
 #include "elf.c"
src/elf64.c +1 -0
index 780e2d4..651f218 100644
@@ -37,5 +37,6 @@
 #define Elf_prpsinfo elf_prpsinfo
 #define Elf_Long long
 #define Elf_prstatus elf_prstatus
+#define Elf_Addr Elf64_Addr
 
 #include "elf.c"
#18 lore raw

PATCH 15/33 Updating Offset

In reply to Janani Venkataraman

Updating offset throughout keeping in mind that they need to be PAGE ALIGNED.

---
 src/elf.c |   50 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 49 insertions(+), 1 deletion(-)
src/elf.c +49 -1
index a5a1feb..a101ddf 100644
@@ -39,6 +39,8 @@
 
 #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
 
+#define ALIGN(addr, pagesize) (((addr) + (pagesize) - 1) & ~(pagesize - 1))
+
 /* Default alignment for program headers */
 #ifndef ELF_EXEC_PAGESIZE
 #define ELF_EXEC_PAGESIZE PAGESIZE
@@ -617,7 +619,6 @@ static int get_phdrs(int pid, struct core_proc *cp)
 	cp_phdrs[0].p_offset = 0;
 	cp_phdrs[0].p_vaddr = 0;
 	cp_phdrs[0].p_paddr = 0;
-	/* Will fill the size after filling notes */
 	cp_phdrs[0].p_filesz = 0;
 	cp_phdrs[0].p_memsz = 0;
 
@@ -671,6 +672,50 @@ static int get_phdrs(int pid, struct core_proc *cp)
 	return 0;
 }
 
+/* Updating the Offset */
+static void update_offset(struct core_proc *cp)
+{
+	Elf_Long data_offset = 0;
+	struct mem_note *note = cp->notes;
+	int i;
+	Elf_Phdr *cp_phdrs;
+	Elf_Ehdr *cp_elf;
+
+	cp_elf = (Elf_Ehdr *)cp->elf_hdr;
+	cp_phdrs = (Elf_Phdr *)cp->phdrs;
+
+	/* ELF HEADER */
+	data_offset += sizeof(Elf_Ehdr);
+
+	/* Program Headers */
+	data_offset += cp->phdrs_count * sizeof(Elf_Phdr);
+
+	/* Notes */
+	cp_phdrs[0].p_offset = data_offset;
+
+	/* Calucalating entire NOTES size */
+	while (note) {
+		data_offset += note->size;
+		note = note->next;
+	}
+
+	/* Filling PT_NOTE size */
+	cp_phdrs[0].p_filesz = data_offset - cp_phdrs[0].p_offset;
+
+	data_offset = ALIGN(data_offset, ELF_EXEC_PAGESIZE);
+
+	/* Populating offsets of Program Headers */
+	for (i = 1; i < cp->phdrs_count; i++) {
+		cp_phdrs[i].p_offset = data_offset;
+		data_offset += cp_phdrs[i].p_filesz;
+		data_offset = ALIGN(data_offset, ELF_EXEC_PAGESIZE);
+	}
+
+	/* Filling extra program header offset if phnum > PN_XNUM */
+	if (cp->phdrs_count > PN_XNUM)
+		cp_elf->e_shoff = data_offset;
+}
+
 int do_elf_coredump(int pid, struct core_proc *cp)
 {
 	int ret, i;
@@ -705,5 +750,8 @@ int do_elf_coredump(int pid, struct core_proc *cp)
 	if (ret)
 		return -1;
 
+	/* Updating offset */
+	update_offset(cp);
+
 	return 0;
 }
#19 lore raw

PATCH 16/33 Writing to core file

In reply to Janani Venkataraman

Writing the core details onto the corefile namely the ELF_HEADER, the PROGRAM_HEADERS, the NOTES and the actual MEMORY.

---
 src/coredump.c |    5 ++++
 src/coredump.h |    1 +
 src/elf.c      |   70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 76 insertions(+)
src/coredump.c +5 -0
index 5cccb7a..a6bcaae 100644
@@ -206,6 +206,9 @@ int do_coredump(int pid, char *core_file)
 	if (ret == -1)
 		goto cleanup;
 
+	/* Initialise core file name */
+	cp.corefile = core_file;
+
 	/* Do elf_dump */
 	if (cp.elf_class == ELFCLASS32)
 		ret = do_elf32_coredump(pid, &cp);
@@ -295,6 +298,8 @@ int main(int argc, char *argv[])
 
 		if (ret == -1)
 			gencore_log("Failed to create core file.\n");
+		else
+			fprintf(stdout, "Created core file.\n");
 	}
 
 	return ret;
src/coredump.h +1 -0
index a729f16..334d930 100644
@@ -70,4 +70,5 @@ struct core_proc {
 	struct mem_note *notes;		/* Head of Notes */
 	void *shdrs;			/* Extra Program Headers */
 	void *phdrs;			/* Program Headers */
+	char *corefile;			/* Name of the core file */
 };
src/elf.c +70 -0

81 lines not on this page · raw message

#20 lore raw

PATCH 17/33 Daemonizing the Process

In reply to Janani Venkataraman

For the purpose of a self dump, we start a daemon which accepts requests and acts like a server. This method was adapted from the CRIU self dump application.

Checks if the process is running as root, if yes daemonizes it through the daemon library call and opens a log file to log in the events of the daemon.

---
 src/coredump.c |   35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)
src/coredump.c +35 -0
index a6bcaae..09fb8da 100644
@@ -29,6 +29,9 @@
 #include <dirent.h>
 #include <errno.h>
 #include <elf.h>
+#include <errno.h>
+#include <syslog.h>
+#include <signal.h>
 #include <sys/ptrace.h>
 #include <coredump.h>
 
@@ -50,6 +53,13 @@ void gencore_log(char *fmt, ...)
 /* Core process object */
 struct core_proc cp;
 
+#ifndef GENCORE_DAEMON_LOGFILE
+#define GENCORE_DAEMON_LOGFILE "/var/log/gencored.log"
+#endif
+
+/* PID of Daemon */
+int pid_log;
+
 /* Initialised core process members */
 void init_core(void)
 {
@@ -248,6 +258,31 @@ cleanup:
 /* Daemon for self dump */
 int daemon_dump(void)
 {
+	/* Check if daemon is running as root */
+	if (geteuid()) {
+		fprintf(stderr, "Run the daemon as root.\n");
+		return -1;
+	}
+
+	/* Daemonizing it */
+	if (daemon(0, 0)) {
+		fprintf(stderr, "Daemon not up %s.", strerror(errno));
+		return -1;
+	}
+
+	/* Get the PID of the daemon */
+	pid_log = getpid();
+
+	fp_log = fopen(GENCORE_DAEMON_LOGFILE, "w+");
+	if (fp_log == NULL) {
+		openlog("gencore_daemon_log", LOG_PID|LOG_CONS, LOG_USER);
+		syslog(LOG_DAEMON, "Could not open: %s.\n",
+					GENCORE_DAEMON_LOGFILE);
+		closelog();
+		return -1;
+	}
+
+	fclose(fp_log);
 	return 0;
 }
 
#21 lore raw

PATCH 18/33 Socket operations

In reply to Janani Venkataraman

Creates a file socket using socket passing PF_UNIX, binds and then listens on the connection.

---
 src/Makefile.am |    3 +-
 src/coredump.c  |  110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 112 insertions(+), 1 deletion(-)
src/Makefile.am +2 -1
index 90d8b25..b9b2e9b 100644
@@ -1,6 +1,7 @@
 AUTOMAKE_OPTIONS = foreign
 HAVE_SYSTEMD_SOCKET_SUPPORT = 0
-CFLAGS += -I. -DHAVE_SYSTEMD_SOCKET_SUPPORT='$(HAVE_SYSTEMD_SOCKET_SUPPORT)'
+SOCKET_PATH = /var/run/gencored.socket
+CFLAGS += -I. -DHAVE_SYSTEMD_SOCKET_SUPPORT='$(HAVE_SYSTEMD_SOCKET_SUPPORT)' -DSOCKET_PATH='"$(SOCKET_PATH)"'
 
 bin_PROGRAMS = gencore
 gencore_SOURCES = coredump.c proc.c elf32.c elf64.c
src/coredump.c +110 -0

134 lines not on this page · raw message

#22 lore raw

PATCH 19/33 Block till request

In reply to Janani Venkataraman

Block until input arrives on the socket.

---
 src/coredump.c |   45 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)
src/coredump.c +45 -0
index c992c66..d93f4b2 100644
@@ -349,6 +349,38 @@ int setup_server(void)
 	return 0;
 }
 
+/* Blocking on a request */
+int block_on_request(void)
+{
+	fd_set read;
+
+	do {
+		/* Initialise */
+		FD_ZERO(&read);
+		FD_SET(socket_fd, &read);
+
+		gencore_log("[%d]: Waiting on incoming request.\n", pid_log);
+
+		if (select(socket_fd + 1, &read, NULL, NULL, NULL) <= 0) {
+			/*
+			 * EINTR just means a signal is caught and hence, we need not
+			 * terminate for this error.
+			 */
+			if (errno != EINTR) {
+				gencore_log("[%d]: Error while waiting for connection requests.\n",
+						pid_log, strerror(errno));
+				close(socket_fd);
+				return -1;
+			}
+		}
+
+	} while(FD_ISSET(socket_fd, &read) == 0);
+
+	gencore_log("[%d]: Request found.\n", pid_log);
+
+	return 0;
+}
+
 /* Daemon for self dump */
 int daemon_dump(void)
 {
@@ -386,6 +418,19 @@ int daemon_dump(void)
 	/* Flush the log */
 	fflush(fp_log);
 
+	while (1) {
+
+		/* Blocks on request */
+		ret = block_on_request();
+		if (ret)
+			goto cleanup;
+
+		/* Flush the log */
+		fflush(fp_log);
+	}
+
+	return 0;
+
 cleanup:
 
 	fclose(fp_log);
#23 lore raw

PATCH 20/33 Handling Requests

In reply to Janani Venkataraman

Accepting requests and then spawning a new thread to service them.

---
 src/coredump.c |  110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)
 mode change 100644 => 100755 src/coredump.c
src/coredump.c +110 -0
old mode 100644
new mode 100755
index d93f4b2..fadb7cd
@@ -42,6 +42,11 @@
 /* Main Socket */
 int socket_fd;
 
+/* Accepted Socket */
+int new_sock;
+
+#define CORE_FILE_NAME_SZ 1000	/* Size of core file name */
+
 /* For logging all the messages */
 FILE *fp_log;
 
@@ -381,6 +386,100 @@ int block_on_request(void)
 	return 0;
 }
 
+/* Handles a SIGCHILD */
+void sigchild_handler(int sig)
+{
+	int pid;
+
+	pid = waitpid(0, &status, WNOHANG);
+
+	gencore_log("[%d]: Request handled by child with PID:%d and exited.\n",
+				pid_log, pid);
+}
+
+/* Sends message to client */
+int send_reply(int err)
+{
+	if (write(new_sock, &err , sizeof(err)) == -1) {
+		gencore_log("[%d]: Could not send message:%s\n",
+					pid_log, strerror(errno));
+		return -1;
+	}
+
+	gencore_log("[%d]: Message sent:%d\n", pid_log, err);
+
+	return 0;
+}
+
+/* Receive message from client */
+int receive_core_filename(char *core_file)
+{
+	memset(core_file, 0, CORE_FILE_NAME_SZ);
+	if (read(new_sock, core_file , CORE_FILE_NAME_SZ) == -1) {
+		send_reply(errno);
+		gencore_log("[%d]: Could not get Core file name:%s\n",
+					pid_log, strerror(errno));
+		return -1;
+	}
+
+	gencore_log("[%d]: Core file path received:%s\n", pid_log,
+						core_file);
+	/* Sending the acknowledgment */
+	send_reply(0);
+
+	return 0;
+}
+
+/* Services requests */
+int service_request(void)
+{
+	int ret;
+	char core_file[CORE_FILE_NAME_SZ];
+
+	/* Receive the message */
+	ret = receive_core_filename(core_file);
+	if (ret)
+		goto cleanup;
+
+cleanup:
+	close(new_sock);
+	if (ret == -1)
+		exit(EXIT_FAILURE);
+
+	exit(EXIT_SUCCESS);
+}
+
+/* Handles new requests */
+int handle_request(void)
+{
+	int pid_new;
+	struct sockaddr_un client_address;
+	socklen_t client_size = sizeof(client_address);
+
+	new_sock = accept(socket_fd, (struct sockaddr *) &client_address,
+							&client_size);
+	if (new_sock < 0) {
+		gencore_log("[%d]: Could not accept:%s\n", pid_log,
+							strerror(errno));
+		close(socket_fd);
+		fclose(fp_log);
+		return -1;
+	}
+
+	gencore_log("[%d]: Accepted.\n", pid_log);
+
+	gencore_log("[%d]: Handling request.\n", pid_log);
+
+	/* New thread to service request */
+	pid_new = fork();
+	if (pid_new == 0) {
+		pid_log = getpid();
+		service_request();
+	}
+
+	return 0;
+}
+
 /* Daemon for self dump */
 int daemon_dump(void)
 {
@@ -418,6 +517,9 @@ int daemon_dump(void)
 	/* Flush the log */
 	fflush(fp_log);
 
+	/* SIGCHILD - Signal handler */
+	signal(SIGCHLD, sigchild_handler);
+
 	while (1) {
 
 		/* Blocks on request */
@@ -427,6 +529,14 @@ int daemon_dump(void)
 
 		/* Flush the log */
 		fflush(fp_log);
+
+		/* Handle new connections */
+		ret = handle_request();
+		if (ret)
+			goto cleanup;
+
+		/* Flush the log */
+		fflush(fp_log);
 	}
 
 	return 0;
#24 lore raw

PATCH 21/33 Get Clients PID

In reply to Janani Venkataraman

Get the Clients PID using getsockopt. This is done for security reasons. So that we dont get any malicious dump requests.

---
 src/coredump.c |   22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
src/coredump.c +22 -0
index fadb7cd..599a912 100755
@@ -22,6 +22,7 @@
  *      Suzuki K. Poulose <[email protected]>
  */
 
+#define _GNU_SOURCE
 #include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
@@ -430,17 +431,38 @@ int receive_core_filename(char *core_file)
 	return 0;
 }
 
+/* Get client details */
+int get_client_pid(struct ucred *client_info)
+{
+	socklen_t len = sizeof(struct ucred);
+	if (getsockopt(new_sock, SOL_SOCKET, SO_PEERCRED,
+				client_info, &len)) {
+		send_reply(errno);
+		gencore_log("[%d]: Can't get credentials of the client:%s\n",
+				pid_log, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
 /* Services requests */
 int service_request(void)
 {
 	int ret;
 	char core_file[CORE_FILE_NAME_SZ];
+	struct ucred client_info;
 
 	/* Receive the message */
 	ret = receive_core_filename(core_file);
 	if (ret)
 		goto cleanup;
 
+	/* Fetch client PID */
+	ret = get_client_pid(&client_info);
+	if (ret)
+		goto cleanup;
+
 cleanup:
 	close(new_sock);
 	if (ret == -1)
#25 lore raw

PATCH 22/33 Dump the task

In reply to Janani Venkataraman

Now we have the PID and the absolute or relative path of the core. We now call the do_coredump function with the appropriate parameters.

---
 src/coredump.c |   60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)
src/coredump.c +60 -0
index 599a912..72ba8d8 100755
@@ -446,6 +446,61 @@ int get_client_pid(struct ucred *client_info)
 	return 0;
 }
 
+/* Dumps the client process */
+int dump_task(struct ucred *client_info, char *core_file)
+{
+
+	char filename[40], cwd[CORE_FILE_NAME_SZ], corefile[CORE_FILE_NAME_SZ];
+	memset(filename, 0, 40);
+	memset(cwd, 0, CORE_FILE_NAME_SZ);
+	memset(corefile, 0, CORE_FILE_NAME_SZ);
+
+	if (setgid(client_info->gid)) {
+		send_reply(errno);
+		gencore_log("[%d]: Could not change GID:%s\n",
+				pid_log, strerror(errno));
+		return -1;
+	}
+
+	if (setuid(client_info->uid)) {
+		send_reply(errno);
+		gencore_log("[%d]: Could not change UID:%s\n",
+				pid_log, strerror(errno));
+		return -1;
+	}
+
+	/* Checking if UID was changed */
+	if (geteuid() != client_info->uid) {
+		send_reply(errno);
+		gencore_log("[%d]: Could not change UID:%s\n",
+				pid_log, strerror(errno));
+		return -1;
+	}
+
+	/* Converting the path to absolute path */
+	if (core_file[0] != '/') {
+		snprintf(filename, 40, "/proc/%d/cwd", client_info->pid);
+		readlink(filename, cwd, CORE_FILE_NAME_SZ);
+		snprintf(corefile, CORE_FILE_NAME_SZ, "%s/%s", cwd, core_file);
+	} else
+		strcpy(corefile, core_file);
+
+	if (do_coredump(client_info->pid, (char *)corefile)) {
+		send_reply(errno);
+		gencore_log("[%d]: Could not create core file %s.\n",
+						pid_log, corefile);
+		fflush(fp_log);
+		return -1;
+	}
+
+	gencore_log("[%d]: Core file %s created.\n", pid_log,
+							corefile);
+
+	send_reply(0);
+
+	return 0;
+}
+
 /* Services requests */
 int service_request(void)
 {
@@ -463,6 +518,11 @@ int service_request(void)
 	if (ret)
 		goto cleanup;
 
+	/* Dump process */
+	ret = dump_task(&client_info, core_file);
+	if (ret)
+		goto cleanup;
+
 cleanup:
 	close(new_sock);
 	if (ret == -1)
#26 lore raw

PATCH 23/33 Handling SIG TERM of the daemon

In reply to Janani Venkataraman

We handle SIGTERM on the daemon, by closing the socket opened for that connection.

---
 src/coredump.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)
src/coredump.c +16 -0
index 72ba8d8..c0da457 100755
@@ -562,6 +562,17 @@ int handle_request(void)
 	return 0;
 }
 
+/* Handles Signals to the Daemon */
+void sig_daemon_handler(int sig)
+{
+	close(socket_fd);
+	unlink(SOCKET_PATH);
+
+	gencore_log("[%d]: Cleanup done and daemon exiting.\n", pid_log);
+
+	fclose(fp_log);
+}
+
 /* Daemon for self dump */
 int daemon_dump(void)
 {
@@ -602,6 +613,11 @@ int daemon_dump(void)
 	/* SIGCHILD - Signal handler */
 	signal(SIGCHLD, sigchild_handler);
 
+	/* Terminate Daemon - signal handler */
+	signal(SIGTERM, sig_daemon_handler);
+	signal(SIGSEGV, sig_daemon_handler);
+	signal(SIGPIPE, sig_daemon_handler);
+
 	while (1) {
 
 		/* Blocks on request */
#27 lore raw

PATCH 24/33 Handling SIG TERM of the child

In reply to Janani Venkataraman

We handle SIGTERM on the child, by sending a message to the client and then closing the socket opened for that connection.

---
 src/coredump.c |   17 +++++++++++++++++
 1 file changed, 17 insertions(+)
src/coredump.c +17 -0
index c0da457..ab120b9 100755
@@ -501,6 +501,18 @@ int dump_task(struct ucred *client_info, char *core_file)
 	return 0;
 }
 
+/* Handles signals to the child */
+void sig_handler_service_proc(int sig)
+{
+	if (sig != SIGPIPE)
+		send_reply(EINTR);
+	close(new_sock);
+
+	gencore_log("[%d]: Cleanup done and child exiting.\n", pid_log);
+
+	fflush(fp_log);
+}
+
 /* Services requests */
 int service_request(void)
 {
@@ -508,6 +520,11 @@ int service_request(void)
 	char core_file[CORE_FILE_NAME_SZ];
 	struct ucred client_info;
 
+	/* Handles stopping of the servicing process */
+	signal(SIGTERM, sig_handler_service_proc);
+	signal(SIGSEGV, sig_handler_service_proc);
+	signal(SIGPIPE, sig_handler_service_proc);
+
 	/* Receive the message */
 	ret = receive_core_filename(core_file);
 	if (ret)
#28 lore raw

PATCH 25/33 Systemd Socket ID retrieval

In reply to Janani Venkataraman

The socket created by systemd, waits for connections. Once it accepts a connection, it starts the service which handles the request for a dump.

Quoted 1 line

From this service, we fetch the socket ID and perform the dump.

This is not functional yet, due to the absence of the sd-daemon.h header file. For this we need to have the correct version of systemd which has this socket option enabled.

---
 src/coredump.c |   32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)
src/coredump.c +32 -0
index ab120b9..98a7146 100755
@@ -39,6 +39,9 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <coredump.h>
+#if HAVE_SYSTEMD_SOCKET_SUPPORT
+#include <sd-daemon.h>
+#endif
 
 /* Main Socket */
 int socket_fd;
@@ -670,6 +673,35 @@ cleanup:
 /* Systemd socket for self dump */
 int socket_dump(void)
 {
+	int n, ret;
+	char core_file[CORE_FILE_NAME_SZ];
+	struct ucred client_info;
+
+	/* Fetching the Socket ID from systemd */
+	n = sd_listen_fds(0);
+	if (n > 1) {
+		gencore_log("Too many file descriptors received.\n");
+		return -1;
+	} else if (n == 1)
+		new_sock = SD_LISTEN_FDS_START + 0;
+	else
+		return -1;
+
+	/* Receive the message */
+	ret = receive_core_filename(core_file);
+	if (ret)
+		return -1;
+
+	/* Fetch client PID */
+	ret = get_client_pid(&client_info);
+	if (ret)
+		return -1;
+
+	/* Dump process */
+	ret = dump_task(&client_info, core_file);
+	if (ret)
+		return -1;
+
 	return 0;
 }
 #endif
#29 lore raw

PATCH 26/33 Setting up Connection

In reply to Janani Venkataraman

Creating a socket and setting up a connection with the server(daemon).

---
 src/Makefile.am |    4 +++
 src/client.c    |   70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 74 insertions(+)
 create mode 100644 src/client.c
src/Makefile.am +4 -0
index b9b2e9b..a1d57ca 100644
@@ -3,6 +3,10 @@ HAVE_SYSTEMD_SOCKET_SUPPORT = 0
 SOCKET_PATH = /var/run/gencored.socket
 CFLAGS += -I. -DHAVE_SYSTEMD_SOCKET_SUPPORT='$(HAVE_SYSTEMD_SOCKET_SUPPORT)' -DSOCKET_PATH='"$(SOCKET_PATH)"'
 
+lib_LTLIBRARIES = libgencore.la
+libgencore_la_LDFLAGS = -fPIC
+libgencore_la_SOURCES = client.c
+
 bin_PROGRAMS = gencore
 gencore_SOURCES = coredump.c proc.c elf32.c elf64.c
 
src/client.c new file +70 -0

70 lines not on this page · raw message

#30 lore raw

PATCH 27/33 Request for dump

In reply to Janani Venkataraman

Request for a self dump and wait for an acknowledgement first and then a response about the status of the dump.

  --------    gencore(file_name) --------
 |        |   ----------------> |        |
 |        |   ack(errno)        |        |
 | CLIENT |   <---------------- | SERVER |
 |        |   status(errno)     |(daemon)|
 |        |   <---------------- |        |

-------- --------

a) The client connects to the server(daemon)

b) Once the connection is accepted, request for dump is sent. This request contains the file name where the dump is to be saved.

c) Once the request is sent, the client waits for an acknowledgment from the server, that it has received the message.

d) Then the client waits for the status response from the server which either is a sucess or a failure. If it is a failure, the correct errno is sent back.

---
 src/Makefile.am |    1 +
 src/client.c    |   51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/gencore.h   |    1 +
 3 files changed, 53 insertions(+)
 create mode 100644 src/gencore.h
src/Makefile.am +1 -0
index a1d57ca..c8a52b7 100644
@@ -3,6 +3,7 @@ HAVE_SYSTEMD_SOCKET_SUPPORT = 0
 SOCKET_PATH = /var/run/gencored.socket
 CFLAGS += -I. -DHAVE_SYSTEMD_SOCKET_SUPPORT='$(HAVE_SYSTEMD_SOCKET_SUPPORT)' -DSOCKET_PATH='"$(SOCKET_PATH)"'
 
+include_HEADERS = gencore.h
 lib_LTLIBRARIES = libgencore.la
 libgencore_la_LDFLAGS = -fPIC
 libgencore_la_SOURCES = client.c
src/client.c +51 -0

69 lines not on this page · raw message

src/gencore.h new file +1 -0
new file mode 100644
index 0000000..8ad0ca8
@@ -0,0 +1 @@
+int gencore(char *corefile);
#31 lore raw

PATCH 28/33 Man pages

In reply to Janani Venkataraman

We have two man pages. One for the command gencore and the second for the library interface(gencore API) which can be called by the client.

---
 doc/gencore.1 |   31 +++++++++++++++++++++++++++++++
 doc/gencore.3 |   28 ++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+)
 create mode 100644 doc/gencore.1
 create mode 100644 doc/gencore.3
doc/gencore.1 new file +31 -0
new file mode 100644
index 0000000..3a7eb8f
@@ -0,0 +1,31 @@
+.\" Manpage for gencore.
+.\" Contact [email protected] to correct errors or typos.
+.TH man 2 "11 Dec 2013" "1.0" "gencore man page"
+.SH NAME
+gencore \- generates coredump
+.SH SYNOPSIS
+gencore pid [output-file-name]
+.SH DESCRIPTION
+gencore takes a dump of the process whose PID is passed
+as a command line argument. If the output file name is
+specified,the core is dumped in that name, if not the
+default filename is core.pid.
+If through options we specify --daemon, in this case the
+gencore command helps in performing a selfdump.Any process
+can connect to this daemon created and request for a dump.
+
+.SH OPTIONS
+--daemon   Creates a daemon to perform selfdumps
+
+--socket   Performs selfdumps with systemd taking care of socket connections.
+	   For this we need to have the correct version of systemd which
+	   has this socket option enabled.
+
+.SH SEE ALSO
+ptrace(2), process_vm_readv(2), gencore(3)
+.SH BUGS
+No known bugs.
+.SH AUTHOR
+Janani Venkataraman <[email protected]>
+
+Suzuki Poulose <[email protected]>
doc/gencore.3 new file +28 -0
new file mode 100644
index 0000000..bc77b10
@@ -0,0 +1,28 @@
+.\" Manpage for libgencore.
+.\" Contact [email protected] to correct errors or typos.
+.TH man 3 "11 Dec 2013" "1.0" "libgencore man page"
+.SH NAME
+gencore \- generates coredump
+.SH SYNOPSIS
+int gencore(char *corefile);
+.SH DESCRIPTION
+gencore takes a dump of the process who sends a message
+to the already running gencore daemon asking for a dump.
+The client or the requester specifies the corefile name
+and the dump is generated in the path specified. This name
+can be a relative or an absolute path.
+.SH RETURN VALUE
+gencore returns 0 on success and the appropriate error
+number on a failure.
+.SH ERRORS
+gencore() may fail and returns the errors for the individual operations
+performed by the daemon or server during the coredump generation.
+.SH OPTIONS
+.SH SEE ALSO
+ptrace(2), process_vm_readv(2), socket(2), listen(2), chmod(2),
+getsockopt(2), setuid(2), setgid(2), geteuid(2), daemon(3),
+bind(2), accept(2).
+.SH BUGS
+No known bugs.
+.SH AUTHOR
+Janani Venkataraman <[email protected]>
#32 lore raw

PATCH 29/33 Automake files for the doc folder

In reply to Janani Venkataraman

Automake files for both gencore command and gencore API.

---
 Makefile.am     |    2 +-
 configure.ac    |    2 +-
 doc/Makefile.am |    2 ++
 src/coredump.c  |    0 
 4 files changed, 4 insertions(+), 2 deletions(-)
 create mode 100644 doc/Makefile.am
 mode change 100755 => 100644 src/coredump.c
Makefile.am +1 -1
index dfa49b2..8b31e4f 100644
@@ -1,2 +1,2 @@
 AUTOMAKE_OPTIONS = foreign
-SUBDIRS = src
+SUBDIRS = src doc
configure.ac +1 -1
index 8c785cd..9e130e0 100644
@@ -4,4 +4,4 @@ AM_INIT_AUTOMAKE([-Wall -Werror foreign])
 AC_CONFIG_HEADERS([config.h])
 AC_PROG_CC
 AC_CONFIG_FILES(Makefile)
-AC_OUTPUT(src/Makefile)
+AC_OUTPUT(src/Makefile doc/Makefile)
doc/Makefile.am new file +2 -0
new file mode 100644
index 0000000..a1cf9b5
@@ -0,0 +1,2 @@
+man1_MANS = gencore.1
+man3_MANS = gencore.3
src/coredump.c +0 -0
old mode 100755
new mode 100644
#33 lore raw

PATCH 30/33 README, COPYING, Changelog

In reply to Janani Venkataraman

Adding the README, COPYING and ChangeLog for the package.

---
 COPYING            |   24 ++++++++++++
 COPYING.LIBGENCORE |   24 ++++++++++++
 Changelog          |    7 +++
 README             |  108 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 163 insertions(+)
 create mode 100644 COPYING
 create mode 100644 COPYING.LIBGENCORE
 create mode 100644 Changelog
 create mode 100644 README
COPYING new file +24 -0
new file mode 100644
index 0000000..a4163ed
@@ -0,0 +1,24 @@
+-------
+GENCORE
+-------
+
+Gencore produces a coredump of a process non-disruptively
+
+Copyright (C) IBM Corporation, 2013 
+
+Authors:
+	Janani Venkataraman <[email protected]>
+
+This package is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This package 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
COPYING.LIBGENCORE new file +24 -0
new file mode 100644
index 0000000..e1a7bf6
@@ -0,0 +1,24 @@
+----------
+LIBGENCORE
+----------
+
+Libencore sets up the interface for requesting a selfdump.
+
+Copyright (C) IBM Corporation, 2013 
+
+Authors:
+	Janani Venkataraman <[email protected]>
+
+This package 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 3 of the License, or
+(at your option) any later version.
+
+This package 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 program; if not, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Changelog new file +7 -0
new file mode 100644
index 0000000..0c780ca
@@ -0,0 +1,7 @@
+---------
+Changelog
+---------
+
+* Thursday Dec 19 2013 Janani Venkataraman <[email protected]> 1.0
+
+- Initial Version
README new file +108 -0

108 lines not on this page · raw message

#34 lore raw

PATCH 31/33 Spec file

In reply to Janani Venkataraman

Adding the Spec file for the package

---
 Makefile.am      |    5 ++++
 configure.ac     |    2 +-
 gencore.spec.in  |   67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 libgencore.pc.in |    8 ++++++
 4 files changed, 81 insertions(+), 1 deletion(-)
 create mode 100644 gencore.spec.in
 create mode 100644 libgencore.pc.in
Makefile.am +5 -0
index 8b31e4f..30e86f5 100644
@@ -1,2 +1,7 @@
 AUTOMAKE_OPTIONS = foreign
 SUBDIRS = src doc
+
+EXTRA_DIST = src/coredump.h src/elf.c src/elf-compat.h doc/gencore.1 doc/gencore.3 Changelog COPYING.LIBGENCORE
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libgencore.pc
configure.ac +1 -1
index 9e130e0..6245ae1 100644
@@ -3,5 +3,5 @@ LT_INIT()
 AM_INIT_AUTOMAKE([-Wall -Werror foreign])
 AC_CONFIG_HEADERS([config.h])
 AC_PROG_CC
-AC_CONFIG_FILES(Makefile)
+AC_CONFIG_FILES(Makefile gencore.spec libgencore.pc)
 AC_OUTPUT(src/Makefile doc/Makefile)
gencore.spec.in new file +67 -0

67 lines not on this page · raw message

libgencore.pc.in new file +8 -0
new file mode 100644
index 0000000..b24dc0e
@@ -0,0 +1,8 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libgencore
+Libs:-L${libdir} -lgencore
+Cflags: -I${includedir}
#35 lore raw

PATCH 32/33 Socket and Service files.

In reply to Janani Venkataraman

The socket and service files for systemd are created.

---
 Makefile.am      |   15 +++++++++++++++
 configure.ac     |    1 +
 gencore.service  |    9 +++++++++
 gencore.socket   |   10 ++++++++++
 gencore.spec.in  |   21 +++++++++++++++++++++
 [email protected] |    9 +++++++++
 6 files changed, 65 insertions(+)
 create mode 100644 gencore.service
 create mode 100644 gencore.socket
 create mode 100644 [email protected]
Makefile.am +15 -0
index 30e86f5..4c6c269 100644
@@ -3,5 +3,20 @@ SUBDIRS = src doc
 
 EXTRA_DIST = src/coredump.h src/elf.c src/elf-compat.h doc/gencore.1 doc/gencore.3 Changelog COPYING.LIBGENCORE
 
+if HAVE_SYSTEMD_SOCKET_SUPPORT 
+EXTRA_DIST += gencore.socket [email protected]
+else
+EXTRA_DIST += gencore.service
+endif
+
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libgencore.pc
+
+systemddir = /usr/lib/systemd
+systemdir = /usr/lib/systemd/system
+
+if HAVE_SYSTEMD_SOCKET_SUPPORT
+system_DATA = [email protected] gencore.socket
+else
+system_DATA = gencore.service
+endif
configure.ac +1 -0
index 6245ae1..39d7902 100644
@@ -4,4 +4,5 @@ AM_INIT_AUTOMAKE([-Wall -Werror foreign])
 AC_CONFIG_HEADERS([config.h])
 AC_PROG_CC
 AC_CONFIG_FILES(Makefile gencore.spec libgencore.pc)
+AM_CONDITIONAL(HAVE_SYSTEMD_SOCKET_SUPPORT, test "have_systemd_socket_support" = false)
 AC_OUTPUT(src/Makefile doc/Makefile)
gencore.service new file +9 -0
new file mode 100644
index 0000000..79220ff
@@ -0,0 +1,9 @@
+[Unit]
+Description=Gencore service
+
+[Service]
+Type=forking
+ExecStart=/usr/bin/gencore --daemon
+
+[Install]
+WantedBy=multi-user.target
gencore.socket new file +10 -0
new file mode 100644
index 0000000..b9afce8
@@ -0,0 +1,10 @@
+[Unit]
+Description=Gencore socket
+
+[Socket]
+ListenStream=/var/run/gencored.socket
+Accept=true
+
+[Install]
+WantedBy=socket.target
+
gencore.spec.in +21 -0
index 58c699e..3937edb 100644
@@ -1,3 +1,5 @@
+%define HAVE_SYSTEMD_SOCKET_SUPPORT 0
+
 Name: gencore
 Version: @VERSION@
 Release: 0
@@ -39,10 +41,19 @@ make
 %install
 make DESTDIR=$RPM_BUILD_ROOT install
 
+%preun
+systemctl stop gencore.service
+
 %files
 %doc README COPYING Changelog
 %{_bindir}/gencore
 %{_mandir}/man1/gencore.1*
+%if %HAVE_SYSTEMD_SOCKET_SUPPORT
+%{_unitdir}/[email protected]
+%{_unitdir}/gencore.socket
+%else
+%{_unitdir}/gencore.service
+%endif
 
 %files -n libgencore
 %doc COPYING.LIBGENCORE
@@ -56,6 +67,16 @@ make DESTDIR=$RPM_BUILD_ROOT install
 %{_libdir}/libgencore.la
 %{_libdir}/pkgconfig/libgencore.pc
 
+%post
+systemctl --system daemon-reload
+%if %HAVE_SYSTEMD_SOCKET_SUPPORT
+systemctl enable gencore.socket
+systemctl start gencore.socket 
+%else
+systemctl enable gencore.service
+systemctl start gencore.service 
+%endif
+
 %post -n libgencore
 ldconfig -n %{_libdir}/libgencore.so.*
 
[email protected] new file +9 -0
new file mode 100644
index 0000000..4dcfb1c
@@ -0,0 +1,9 @@
+[Unit]
+Description=Gencore service
+
+[Service]
+Type=forking
+ExecStart=/usr/bin/gencore --socket
+
+[Install]
+WantedBy=multi-user.target
#36 lore raw

PATCH 33/33 Support check

In reply to Janani Venkataraman

This checks if a particular build can support dumps on 32/64 bit applications.

For eg. A 32bit build of gencore cant support dumps on 64 bit applications.

---
 src/coredump.c |   11 +++++++++++
 src/elf64.c    |    2 ++
 2 files changed, 13 insertions(+)
src/coredump.c +11 -0
index 98a7146..49af7b3 100644
@@ -76,6 +76,17 @@ struct core_proc cp;
 /* PID of Daemon */
 int pid_log;
 
+/* Support Check */
+static int support_check(int pid, struct core_proc *cp)
+{
+	gencore_log("This build of gencore(32bit) cannot dump a 64bit process.\n");
+	return -1;
+}
+
+#if defined(__i386) || (defined(__s390__) && !defined(__s390x__)) || (defined(__PPC__) && !defined(__PPC64__))
+#define do_elf64_coredump support_check
+#endif
+
 /* Initialised core process members */
 void init_core(void)
 {
src/elf64.c +2 -0
index 651f218..11c8c76 100644
@@ -28,6 +28,7 @@
  * and operations once elf.c is included.
  */
 
+#if defined(__x86_64) || (defined(__s390x__) && defined(__s390__)) || (defined(__PPC64__) && defined(__PPC__))
 #define do_elf_coredump do_elf64_coredump
 
 #define Elf_Ehdr Elf64_Ehdr
@@ -40,3 +41,4 @@
 #define Elf_Addr Elf64_Addr
 
 #include "elf.c"
+#endif
#37 lore raw
In reply to Janani Venkataraman
Quoted 81 lines · Janani Venkataraman

Hi all,

The following series implements an infrastructure for capturing the core of an application without disrupting its process.

Kernel Space Approach:

1) Posted an RFD to LKML explaining the various kernel-methods being analysed.

https://lkml.org/lkml/2013/9/3/122

2) Went ahead to implement the same using the task_work_add approach and posted an RFC to LKML.

http://lwn.net/Articles/569534/

Based on the responses, the present approach implements the same in User-Space.

User Space Approach:

We didn't adopt the CRIU approach because our method would give us a head start, as all that the distro would need is the PTRACE_functionality and nothing more which is available from kernel versions 3.4 and above.

Basic Idea of User Space:

1) The threads are held using PTRACE_SEIZE and PTRACE_INTERRUPT.

2) The dump is then taken using the following:
1) The register sets namely general purpose, floating point and the arch specific register sets are collected through PTRACE_GETREGSET calls by passing the appropriate register type as parameter.
2) The virtual memory maps are collected from /proc/pid/maps.
3) The auxiliary vector is collected from /proc/pid/auxv.
4) Process state information for filling the notes such as PRSTATUS and PRPSINFO are collected from /proc/pid/stat and /proc/pid/status.
5) The actual memory is read through process_vm_readv syscall as suggested by Andi Kleen.
6) Command line arguments are collected from /proc/pid/cmdline

3) The threads are then released using PTRACE_DETACH.

Self Dump:

A self dump is implemented with the following approach which was adapted from CRIU:

Gencore Daemon

The programs can request a dump using gencore() API, provided through libgencore. This is implemented through a daemon which listens on a UNIX File socket. The daemon is started immediately post installation.

We have provided service scripts for integration with systemd.

NOTE:

On systems with systemd, we could make use of socket option, which will avoid the need for running the gencore daemon always. The systemd can wait on the socket for requests and trigger the daemon as and when required. However, since the systemd socket APIs are not exported yet, we have disabled the supporting code for this feature.

libgencore:

1) The client interface is a standard library call. All that the dump requester does is open the library and call the gencore() API and the dump will be generated in the path specified(relative/absolute).

To Do:

1) Presently we wait indefinitely for the all the threads to seize. We can add a time-out to decide how much time we need to wait for the threads to be seized. This can be passed as command line argument in the case of a third party dump and in the case of the self-dump through the library call. We need to work on how much time to wait.

2) Like mentioned before, the systemd socket APIs are not exported yet and hence this option is disabled now. Once these API's are available we can enable the socket option.

Quoted 7 lines

We would like to push this to one of the following packages:
a) util-linux
b) coreutils
c) procps-ng

We are not sure which one would suit this application the best. Please let us know your views on the same.

Well from coreutils persepective, they're generally non Linux specific _commands_, and so wouldn't be a natural home for this (despite the _core_ in the name :)).

thanks,
Pádraig.

maintainer
#38 lore raw
In reply to Janani Venkataraman
Quoted 5 lines · Janani Venkataraman

Gencore Daemon

The programs can request a dump using gencore() API, provided through libgencore. This is implemented through a daemon which listens on a UNIX File socket. The daemon is started immediately post installation.

The gencore command looks like a good idea, but why we need the client-server infrastructure? At least at first glance it seems like overkill.

Quoted 4 lines

We would like to push this to one of the following packages:
a) util-linux
b) coreutils
c) procps-ng

d) somewhere near to gdb :-)

Karel

Signature · 2 lines
 Karel Zak  <[email protected]>
 http://karelzak.blogspot.com
#39 lore raw

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 3 lines · Karel Zak

The gencore command looks like a good idea, but why we need the client-server infrastructure? At least at first glance it seems like overkill.

Yes, the server seems pointless.

Quoted 4 lines
Quoted 2 lines

We would like to push this to one of the following packages: a) util-linux b) coreutils c) procps-ng

d) somewhere near to gdb :-)

gdb already has such a thing; it's called gcore. Kind of makes this work seem redundant?

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.17 (MingW32)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iQEcBAEBAgAGBQJTLFSjAAoJEI5FoCIzSKrwuTIH/jGtc3dB1WKgtDSE2wtXXHO0 0WxhWDbVkpJ0Q+tkG/tjw4wudYtEJ+XAHsCIK3vH7OMX7i4PMOLDgjT/FyxTPrWl W+eUCazi4vRPNGscii5mRFlsthgEd2pofotlx5UB7Br9D05/QZqErtEMJMqD935V 94wlvJSvJI0n3hfAUgcM8apxTgxYr/TIPEQPfqLem3xTZIw3lTRic613Qji2Kwif l+kqs0WLy/AHEwBLRrXDQahnv5n2IhOVUn3KDwm9Su56rbrmAF7LAvK0+uxRyoQk IVC5h8BdBQ4QMYl7KYYEPy+n+0Qbe1EQ4tLmYvaEBwXP5c0QDGX0M5VUkko+eGI= =zFoW
-----END PGP SIGNATURE-----

#40 lore raw
Quoted 11 lines · Phillip Susi
Quoted 3 lines · Karel Zak

The gencore command looks like a good idea, but why we need the client-server infrastructure? At least at first glance it seems like overkill.

Yes, the server seems pointless.

Quoted 3 lines
Quoted 2 lines

We would like to push this to one of the following packages: a) util-linux b) coreutils c) procps-ng

d) somewhere near to gdb :-)

gdb already has such a thing; it's called gcore. Kind of makes this work seem redundant?

Gcore attaches to the process using gdb and runs the gdb gcore command and then detaches. In gcore the dump cannot be issued from a signal handler context as fork() is not signal safe and moreover it is disruptive in nature as the gdb attaches using ptrace which sends a SIGSTOP signal. Hence the gcore method cannot be used if the process wants to initiate a self dump as well. The following link points to a discussion regarding why not gcore.

https://lkml.org/lkml/2013/9/3/122

Quoted 1 line
#41 lore raw

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 4 lines · Janani Venkataraman

Gcore attaches to the process using gdb and runs the gdb gcore command and then detaches. In gcore the dump cannot be issued from a signal handler context as fork() is not signal safe and moreover it is disruptive in

You can fork+exec from a signal handler just fine.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.17 (MingW32)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iQEcBAEBAgAGBQJTMDknAAoJEI5FoCIzSKrwo2QH/RCmAn404PlO6cZLNuQ5PXqc MdR6U62K/BwZo42Fr694FzzjGKwuqx3YHB8gU+tyE26yZ31reFWG8KpdfV46NRYG sEPH/lJOerMXwfbpxE0YFZBow5wZqTg/XsiXMyz8M4/KdMgOgCEqsaG/IRPvK0+B NiaQmFct0Y2onwoy50mrR2sGeKOgA5510osoChCq+TKUrsCJp80jYvz6bVOi6kwY Oi+bAl/tzcQDy7yHkUqYJXYRf3o9jbHqGxnC/IzygOntggqmIiB8ez4ZDZC2YbOl htv0gP/jqZk8Pm3C/gv5S7xhqjy2+yvkWXg7mhsz7gsbSA4K5LXd5bN5ovEBGAw= =JWwx
-----END PGP SIGNATURE-----

#42 lore raw
Quoted 10 lines · Phillip Susi

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 4 lines · Janani Venkataraman

Gcore attaches to the process using gdb and runs the gdb gcore command and then detaches. In gcore the dump cannot be issued from a signal handler context as fork() is not signal safe and moreover it is disruptive in

You can fork+exec from a signal handler just fine.

Please go through the bug, and especially the comment below :

https://sourceware.org/bugzilla/show_bug.cgi?id=4737#c12

Cheers
Suzuki

#43 lore raw
In reply to Karel Zak
Quoted 9 lines · Karel Zak
Quoted 5 lines · Janani Venkataraman

Gencore Daemon

The programs can request a dump using gencore() API, provided through libgencore. This is implemented through a daemon which listens on a UNIX File socket. The daemon is started immediately post installation.

The gencore command looks like a good idea, but why we need the client-server infrastructure? At least at first glance it seems like overkill.

We are also looking at a process being able to do a self dump. ptrace SEIZE/INTERRUPT cannot be used on one-self and hencewe need to offload the dump another 'process'. Since we can't fork(), as it is not signal safe, we need a dedicated process to handle such requests. Hence the server.

https://lkml.org/lkml/2013/9/3/122

Quoted 8 lines
Quoted 4 lines

We would like to push this to one of the following packages:
a) util-linux
b) coreutils
c) procps-ng

d) somewhere near to gdb :-)

Karel