Add a concept of tasks and leader thread
diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c
index eca3548..b55c5ef 100644
--- a/sysdeps/linux-gnu/proc.c
+++ b/sysdeps/linux-gnu/proc.c
@@ -2,12 +2,19 @@
#include "common.h"
#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <inttypes.h>
#include <link.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/syscall.h>
+
/* /proc/pid doesn't exist just after the fork, and sometimes `ltrace'
* couldn't open it to find the executable. So it may be necessary to
@@ -16,17 +23,19 @@
#define MAX_DELAY 100000 /* 100000 microseconds = 0.1 seconds */
+#define PROC_PID_FILE(VAR, FORMAT, PID) \
+ char VAR[strlen(FORMAT) + 6]; \
+ sprintf(VAR, FORMAT, PID)
+
/*
* Returns a (malloc'd) file name corresponding to a running pid
*/
char *
pid2name(pid_t pid) {
- char proc_exe[1024];
-
if (!kill(pid, 0)) {
int delay = 0;
- sprintf(proc_exe, "/proc/%d/exe", pid);
+ PROC_PID_FILE(proc_exe, "/proc/%d/exe", pid);
while (delay < MAX_DELAY) {
if (!access(proc_exe, F_OK)) {
@@ -38,6 +47,167 @@
return NULL;
}
+static FILE *
+open_status_file(pid_t pid)
+{
+ PROC_PID_FILE(fn, "/proc/%d/status", pid);
+ /* Don't complain if we fail. This would typically happen
+ when the process is about to terminate, and these files are
+ not available anymore. This function is called from the
+ event loop, and we don't want to clutter the output just
+ because the process terminates. */
+ return fopen(fn, "r");
+}
+
+static char *
+find_line_starting(FILE * file, const char * prefix, size_t len)
+{
+ char * line = NULL;
+ size_t line_len = 0;
+ while (!feof(file)) {
+ if (getline(&line, &line_len, file) < 0)
+ return NULL;
+ if (strncmp(line, prefix, len) == 0)
+ return line;
+ }
+ return NULL;
+}
+
+static void
+each_line_starting(FILE * file, const char *prefix,
+ enum pcb_status (*cb)(const char * line, const char * prefix,
+ void * data),
+ void * data)
+{
+ size_t len = strlen(prefix);
+ char * line;
+ while ((line = find_line_starting(file, prefix, len)) != NULL) {
+ enum pcb_status st = (*cb)(line, prefix, data);
+ free (line);
+ if (st == pcb_stop)
+ return;
+ }
+}
+
+static enum pcb_status
+process_leader_cb(const char * line, const char * prefix, void * data)
+{
+ pid_t * pidp = data;
+ *pidp = atoi(line + strlen(prefix));
+ return pcb_stop;
+}
+
+pid_t
+process_leader(pid_t pid)
+{
+ pid_t tgid = pid;
+ FILE * file = open_status_file(pid);
+ if (file != NULL) {
+ each_line_starting(file, "Tgid:\t", &process_leader_cb, &tgid);
+ fclose(file);
+ }
+
+ return tgid;
+}
+
+static enum pcb_status
+process_stopped_cb(const char * line, const char * prefix, void * data)
+{
+ char c = line[strlen(prefix)];
+ // t:tracing stop, T:job control stop
+ *(int *)data = (c == 't' || c == 'T');
+ return pcb_stop;
+}
+
+int
+process_stopped(pid_t pid)
+{
+ int is_stopped = -1;
+ FILE * file = open_status_file(pid);
+ if (file != NULL) {
+ each_line_starting(file, "State:\t", &process_stopped_cb,
+ &is_stopped);
+ fclose(file);
+ }
+ return is_stopped;
+}
+
+static enum pcb_status
+process_status_cb(const char * line, const char * prefix, void * data)
+{
+ *(char *)data = line[strlen(prefix)];
+ return pcb_stop;
+}
+
+char
+process_status(pid_t pid)
+{
+ char ret = '?';
+ FILE * file = open_status_file(pid);
+ if (file != NULL) {
+ each_line_starting(file, "State:\t", &process_status_cb, &ret);
+ fclose(file);
+ }
+ return ret;
+}
+
+static int
+all_digits(const char *str)
+{
+ while (isdigit(*str))
+ str++;
+ return !*str;
+}
+
+int
+process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n)
+{
+ PROC_PID_FILE(fn, "/proc/%d/task", pid);
+ DIR * d = opendir(fn);
+ if (d == NULL)
+ return -1;
+
+ /* XXX This is racy. We need to stop the tasks that we
+ discover this way and re-scan the directory to eventually
+ reach a full set of tasks. */
+ pid_t *tasks = NULL;
+ size_t n = 0;
+ size_t alloc = 0;
+
+ while (1) {
+ struct dirent entry;
+ struct dirent *result;
+ if (readdir_r(d, &entry, &result) != 0) {
+ free(tasks);
+ return -1;
+ }
+ if (result == NULL)
+ break;
+ if (result->d_type == DT_DIR && all_digits(result->d_name)) {
+ pid_t npid = atoi(result->d_name);
+ if (n >= alloc) {
+ alloc = alloc > 0 ? (2 * alloc) : 8;
+ pid_t *ntasks = realloc(tasks,
+ sizeof(*tasks) * alloc);
+ if (ntasks == NULL) {
+ free(tasks);
+ return -1;
+ }
+ tasks = ntasks;
+ }
+ if (n >= alloc)
+ abort();
+ tasks[n++] = npid;
+ }
+ }
+
+ closedir(d);
+
+ *ret_tasks = tasks;
+ *ret_n = n;
+ return 0;
+}
+
static int
find_dynamic_entry_addr(Process *proc, void *pvAddr, int d_tag, void **addr) {
int i = 0, done = 0;
@@ -286,3 +456,14 @@
free(rdbg);
return 0;
}
+
+int
+task_kill (pid_t pid, int sig)
+{
+ // Taken from GDB
+ int ret;
+
+ errno = 0;
+ ret = syscall (__NR_tkill, pid, sig);
+ return ret;
+}