Non disruptive application core dump infrastructure
Subsystems (3)
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
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
@@ -0,0 +1,2 @@ +AUTOMAKE_OPTIONS = foreign +SUBDIRS =
configure.ac +7 -0
@@ -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()
PATCH 2/33 Validity of arguments
In reply to Janani VenkataramanThe 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
@@ -1,2 +1,2 @@ AUTOMAKE_OPTIONS = foreign -SUBDIRS = +SUBDIRS = src
configure.ac +1 -1
@@ -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
@@ -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
PATCH 3/33 Process Status
In reply to Janani VenkataramanFetch 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
@@ -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
@@ -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
@@ -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
PATCH 4/33 Hold threads
In reply to Janani VenkataramanGetting 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
@@ -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
@@ -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
Re: [PATCH 04/33] Hold threads
Quoted 24 lines · Janani Venkataraman
Getting number of threads and their respective IDs through
/proc/pid/statand/proc/pid/task.The threads are then seized and interrupted. After the dump is taken they are detached.
Signed-off-by Janani Venkataraman <[email protected]>---
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; +}
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/statand/proc/pid/task.The threads are then seized and interrupted. After the dump is taken they are detached.
Signed-off-by Janani Venkataraman <[email protected]>---
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; +}
Re: [PATCH 04/33] Hold threads
In reply to Pavel EmelyanovQuoted 38 lines · Pavel Emelyanov
Quoted 23 lines · Janani Venkataraman
Getting number of threads and their respective IDs through
/proc/pid/statand/proc/pid/task.The threads are then seized and interrupted. After the dump is taken they are detached.
Signed-off-by Janani Venkataraman <[email protected]>---
(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
@@ -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
PATCH 5/33 Fetching Memory maps
In reply to Janani VenkataramanFetching 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
@@ -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
@@ -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
PATCH 6/33 Check ELF class
In reply to Janani VenkataramanThe 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
@@ -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
@@ -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
PATCH 7/33 Do elf_coredump
In reply to Janani VenkataramanPerform 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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"
PATCH 8/33 Fills elf header
In reply to Janani VenkataramanPopulates 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
@@ -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
@@ -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
@@ -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
@@ -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"
PATCH 9/33 Adding notes infrastructure
In reply to Janani VenkataramanCreating 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
@@ -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
@@ -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
@@ -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
@@ -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"
PATCH 10/33 Populates PRPS info
In reply to Janani VenkataramanPopulates 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
@@ -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
@@ -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
@@ -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"
PATCH 11/33 Populate AUXV
In reply to Janani VenkataramanPopulate the auxillary vector using /proc/pid/auxv.
--- src/elf.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+)
src/elf.c +73 -0
@@ -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 ; }
PATCH 12/33 Fetch File maps
In reply to Janani VenkataramanThe 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
@@ -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
@@ -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
@@ -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"
PATCH 13/33 Fetching thread specific Notes
In reply to Janani VenkataramanPopulates 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
@@ -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
@@ -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
@@ -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"
PATCH 14/33 Populating Program Headers
In reply to Janani VenkataramanPopulates 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
@@ -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
@@ -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
@@ -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
@@ -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"
PATCH 15/33 Updating Offset
In reply to Janani VenkataramanUpdating 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
@@ -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 ; }
PATCH 16/33 Writing to core file
In reply to Janani VenkataramanWriting 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
@@ -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
@@ -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
PATCH 17/33 Daemonizing the Process
In reply to Janani VenkataramanFor 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
@@ -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 ; }
PATCH 18/33 Socket operations
In reply to Janani VenkataramanCreates 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
@@ -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
PATCH 19/33 Block till request
In reply to Janani VenkataramanBlock until input arrives on the socket.
--- src/coredump.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
src/coredump.c +45 -0
@@ -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);
PATCH 20/33 Handling Requests
In reply to Janani VenkataramanAccepting 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
@@ -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 ;
PATCH 21/33 Get Clients PID
In reply to Janani VenkataramanGet 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
@@ -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 )
PATCH 22/33 Dump the task
In reply to Janani VenkataramanNow 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
@@ -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 )
PATCH 23/33 Handling SIG TERM of the daemon
In reply to Janani VenkataramanWe 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
@@ -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 */
PATCH 24/33 Handling SIG TERM of the child
In reply to Janani VenkataramanWe 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
@@ -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)
PATCH 25/33 Systemd Socket ID retrieval
In reply to Janani VenkataramanThe 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
@@ -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
PATCH 26/33 Setting up Connection
In reply to Janani VenkataramanCreating 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
@@ -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
PATCH 27/33 Request for dump
In reply to Janani VenkataramanRequest 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
@@ -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
@@ -0,0 +1 @@ +int gencore (char *corefile);
PATCH 28/33 Man pages
In reply to Janani VenkataramanWe 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
@@ -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
@@ -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]>
PATCH 29/33 Automake files for the doc folder
In reply to Janani VenkataramanAutomake 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
@@ -1,2 +1,2 @@ AUTOMAKE_OPTIONS = foreign -SUBDIRS = src +SUBDIRS = src doc
configure.ac +1 -1
@@ -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
@@ -0,0 +1,2 @@ +man1_MANS = gencore.1 +man3_MANS = gencore.3
src/coredump.c +0 -0
PATCH 30/33 README, COPYING, Changelog
In reply to Janani VenkataramanAdding 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
@@ -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
@@ -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
@@ -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
PATCH 31/33 Spec file
In reply to Janani VenkataramanAdding 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
@@ -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
@@ -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
@@ -0,0 +1,8 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libgencore +Libs:-L${libdir} -lgencore +Cflags: -I${includedir}
PATCH 32/33 Socket and Service files.
In reply to Janani VenkataramanThe 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,9 @@ +[Unit] +Description=Gencore service + +[Service] +Type=forking +ExecStart=/usr/bin/gencore --socket + +[Install] +WantedBy=multi-user.target
PATCH 33/33 Support check
In reply to Janani VenkataramanThis 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
@@ -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
@@ -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
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_addapproach 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_functionalityand nothing more which is available from kernel versions 3.4 and above.Basic Idea of User Space:
1) The threads are held using
PTRACE_SEIZEandPTRACE_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 throughPTRACE_GETREGSETcalls 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/statand/proc/pid/status.
5) The actual memory is read throughprocess_vm_readvsyscall as suggested by Andi Kleen.
6) Command line arguments are collected from/proc/pid/cmdline3) 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-ngWe 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.
Karel Zak
maintainerQuoted 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
-----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-----
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
-----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-----
Quoted 10 lines · Phillip Susi
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1Quoted 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 inYou 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
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-ngd) somewhere near to gdb :-)
Karel