Commit 2f172c8b authored by Andrey Vagin's avatar Andrey Vagin Committed by Pavel Emelyanov

crtools: split cr-dump.c in two files

Signed-off-by: 's avatarAndrey Vagin <avagin@openvz.org>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 3d1e8c68
...@@ -73,6 +73,7 @@ obj-y += sd-daemon.o ...@@ -73,6 +73,7 @@ obj-y += sd-daemon.o
obj-y += plugin.o obj-y += plugin.o
obj-y += cr-errno.o obj-y += cr-errno.o
obj-y += pie/pie-relocs.o obj-y += pie/pie-relocs.o
obj-y += seize.o
ifneq ($(MAKECMDGOALS),clean) ifneq ($(MAKECMDGOALS),clean)
incdeps := y incdeps := y
......
...@@ -79,11 +79,10 @@ ...@@ -79,11 +79,10 @@
#include "security.h" #include "security.h"
#include "lsm.h" #include "lsm.h"
#include "seccomp.h" #include "seccomp.h"
#include "seize.h"
#include "asm/dump.h" #include "asm/dump.h"
#define NR_ATTEMPTS 5
static char loc_buf[PAGE_SIZE]; static char loc_buf[PAGE_SIZE];
static void close_vma_file(struct vma_area *vma) static void close_vma_file(struct vma_area *vma)
...@@ -713,353 +712,6 @@ err: ...@@ -713,353 +712,6 @@ err:
return ret; return ret;
} }
static int parse_children(pid_t pid, pid_t **_c, int *_n)
{
pid_t *ch = NULL;
int nr = 0;
DIR *dir;
struct dirent *de;
dir = opendir_proc(pid, "task");
if (dir == NULL)
return -1;
while ((de = readdir(dir))) {
int fd, len;
char *pos;
if (dir_dots(de))
continue;
fd = open_proc(pid, "task/%s/children", de->d_name);
if (fd < 0)
goto err;
len = read(fd, loc_buf, sizeof(loc_buf) - 1);
close(fd);
if (len < 0)
goto err;
loc_buf[len] = '\0';
pos = loc_buf;
while (1) {
pid_t val, *tmp;
val = strtol(pos, &pos, 0);
if (!val) {
BUG_ON(*pos != '\0');
break;
}
tmp = xrealloc(ch, (nr + 1) * sizeof(pid_t));
if (!tmp)
goto err;
ch = tmp;
ch[nr] = val;
nr++;
pos++; /* space goes after each pid */
}
}
*_c = ch;
*_n = nr;
closedir(dir);
return 0;
err:
closedir(dir);
xfree(ch);
return -1;
}
static inline bool child_collected(struct pstree_item *i, pid_t pid)
{
struct pstree_item *c;
list_for_each_entry(c, &i->children, sibling)
if (c->pid.real == pid)
return true;
return false;
}
static int collect_task(struct pstree_item *item);
static int collect_children(struct pstree_item *item)
{
pid_t *ch;
int ret, i, nr_children, nr_inprogress;
ret = parse_children(item->pid.real, &ch, &nr_children);
if (ret < 0)
return ret;
nr_inprogress = 0;
for (i = 0; i < nr_children; i++) {
struct pstree_item *c;
pid_t pid = ch[i];
/* Is it already frozen? */
if (child_collected(item, pid))
continue;
nr_inprogress++;
pr_info("Seized task %d, state %d\n", pid, ret);
c = alloc_pstree_item();
if (c == NULL) {
ret = -1;
goto free;
}
ret = seize_task(pid, item->pid.real, &dmpi(c)->pi_creds);
if (ret < 0) {
/*
* Here is a race window between parse_children() and seize(),
* so the task could die for these time.
* Don't worry, will try again on the next attempt. The number
* of attempts is restricted, so it will exit if something
* really wrong.
*/
ret = 0;
xfree(c);
continue;
}
c->pid.real = pid;
c->parent = item;
c->state = ret;
list_add_tail(&c->sibling, &item->children);
/* Here is a recursive call (Depth-first search) */
ret = collect_task(c);
if (ret < 0)
goto free;
}
free:
xfree(ch);
return ret < 0 ? ret : nr_inprogress;
}
static void unseize_task_and_threads(const struct pstree_item *item, int st)
{
int i;
if (item->state == TASK_DEAD)
return;
/*
* The st is the state we want to switch tasks into,
* the item->state is the state task was in when we seized one.
*/
unseize_task(item->pid.real, item->state, st);
if (st == TASK_DEAD)
return;
for (i = 1; i < item->nr_threads; i++)
if (ptrace(PTRACE_DETACH, item->threads[i].real, NULL, NULL))
pr_perror("Unable to detach from %d", item->threads[i].real);
}
static void pstree_wait(struct pstree_item *root_item)
{
struct pstree_item *item = root_item;
int pid, status, i;
for_each_pstree_item(item) {
if (item->state == TASK_DEAD)
continue;
for (i = 0; i < item->nr_threads; i++) {
pid = wait4(-1, &status, __WALL, NULL);
if (pid < 0) {
pr_perror("wait4 failed");
break;
} else {
if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL) {
pr_err("Unexpected exit code %d of %d\n", status, pid);
BUG();
}
}
}
}
pid = wait4(-1, &status, __WALL, NULL);
if (pid > 0) {
pr_err("Unexpected child %d", pid);
BUG();
}
}
static void pstree_switch_state(struct pstree_item *root_item, int st)
{
struct pstree_item *item = root_item;
pr_info("Unfreezing tasks into %d\n", st);
for_each_pstree_item(item)
unseize_task_and_threads(item, st);
if (st == TASK_DEAD)
pstree_wait(root_item);
}
static pid_t item_ppid(const struct pstree_item *item)
{
item = item->parent;
return item ? item->pid.real : -1;
}
static inline bool thread_collected(struct pstree_item *i, pid_t tid)
{
int t;
if (i->pid.real == tid) /* thread leader is collected as task */
return true;
for (t = 0; t < i->nr_threads; t++)
if (tid == i->threads[t].real)
return true;
return false;
}
static int collect_threads(struct pstree_item *item)
{
struct pid *threads = NULL;
int nr_threads = 0, i = 0, ret, nr_inprogress, nr_stopped = 0;
ret = parse_threads(item->pid.real, &threads, &nr_threads);
if (ret < 0)
goto err;
if ((item->state == TASK_DEAD) && (nr_threads > 1)) {
pr_err("Zombies with threads are not supported\n");
goto err;
}
/* The number of threads can't be less than allready frozen */
item->threads = xrealloc(item->threads, nr_threads * sizeof(struct pid));
if (item->threads == NULL)
return -1;
if (item->nr_threads == 0) {
item->threads[0].real = item->pid.real;
item->nr_threads = 1;
}
nr_inprogress = 0;
for (i = 0; i < nr_threads; i++) {
pid_t pid = threads[i].real;
if (thread_collected(item, pid))
continue;
nr_inprogress++;
pr_info("\tSeizing %d's %d thread\n",
item->pid.real, pid);
ret = seize_task(pid, item_ppid(item), &dmpi(item)->pi_creds);
if (ret < 0) {
/*
* Here is a race window between parse_threads() and seize(),
* so the task could die for these time.
* Don't worry, will try again on the next attempt. The number
* of attempts is restricted, so it will exit if something
* really wrong.
*/
continue;
}
BUG_ON(item->nr_threads + 1 > nr_threads);
item->threads[item->nr_threads].real = pid;
item->nr_threads++;
if (ret == TASK_DEAD) {
pr_err("Zombie thread not supported\n");
goto err;
}
if (ret == TASK_STOPPED) {
nr_stopped++;
}
}
if (nr_stopped && nr_stopped != nr_inprogress) {
pr_err("Individually stopped threads not supported\n");
goto err;
}
xfree(threads);
return nr_inprogress;
err:
xfree(threads);
return -1;
}
static int collect_loop(struct pstree_item *item,
int (*collect)(struct pstree_item *))
{
int attempts = NR_ATTEMPTS, nr_inprogress = 1;
/*
* While we scan the proc and seize the children/threads
* new ones can appear (with clone(CLONE_PARENT) or with
* pthread_create). Thus, after one go, we need to repeat
* the scan-and-freeze again collecting new arrivals. As
* new guys may appear again we do NR_ATTEMPTS passes and
* fail to seize the item if new tasks/threads still
* appear.
*/
while (nr_inprogress > 0 && attempts) {
attempts--;
nr_inprogress = collect(item);
}
/*
* We may fail to collect items or run out of attempts.
* In the former case nr_inprogress will be negative, in
* the latter -- positive. Thus it's enough just to check
* for "no more new stuff" and say "we're OK" if so.
*/
return (nr_inprogress == 0) ? 0 : -1;
}
static int collect_task(struct pstree_item *item)
{
int ret;
ret = collect_loop(item, collect_threads);
if (ret < 0)
goto err_close;
/* Depth-first search (DFS) is used for traversing a process tree. */
ret = collect_loop(item, collect_children);
if (ret < 0)
goto err_close;
if ((item->state == TASK_DEAD) && !list_empty(&item->children)) {
pr_err("Zombie with children?! O_o Run, run, run!\n");
goto err_close;
}
if (pstree_alloc_cores(item))
goto err_close;
pr_info("Collected %d in %d state\n", item->pid.real, item->state);
return 0;
err_close:
close_pid_proc();
return -1;
}
static int collect_pstree_ids_predump(void) static int collect_pstree_ids_predump(void)
{ {
struct pstree_item *item; struct pstree_item *item;
...@@ -1101,36 +753,6 @@ int collect_pstree_ids(void) ...@@ -1101,36 +753,6 @@ int collect_pstree_ids(void)
return 0; return 0;
} }
static int collect_pstree(pid_t pid)
{
int ret;
timing_start(TIME_FREEZING);
root_item = alloc_pstree_item();
if (root_item == NULL)
return -1;
root_item->pid.real = pid;
ret = seize_task(pid, -1, &dmpi(root_item)->pi_creds);
if (ret < 0)
goto err;
pr_info("Seized task %d, state %d\n", pid, ret);
root_item->state = ret;
ret = collect_task(root_item);
if (ret < 0)
goto err;
timing_stop(TIME_FREEZING);
timing_start(TIME_FROZEN);
return 0;
err:
pstree_switch_state(root_item, TASK_ALIVE);
return -1;
}
static int collect_file_locks(void) static int collect_file_locks(void)
{ {
return parse_file_locks(); return parse_file_locks();
......
...@@ -242,4 +242,6 @@ int parse_cgroups(struct list_head *cgroups, unsigned int *n_cgroups); ...@@ -242,4 +242,6 @@ int parse_cgroups(struct list_head *cgroups, unsigned int *n_cgroups);
/* callback for AUFS support */ /* callback for AUFS support */
extern int aufs_parse(struct mount_info *mi); extern int aufs_parse(struct mount_info *mi);
int parse_children(pid_t pid, pid_t **_c, int *_n);
#endif /* __CR_PROC_PARSE_H__ */ #endif /* __CR_PROC_PARSE_H__ */
#ifndef __CR_SEIZE_H__
#define __CR_SEIZE_H__
extern int collect_pstree(pid_t pid);
extern void pstree_switch_state(struct pstree_item *root_item, int st);
#endif
...@@ -2077,3 +2077,64 @@ bool proc_status_creds_eq(struct proc_status_creds *o1, struct proc_status_creds ...@@ -2077,3 +2077,64 @@ bool proc_status_creds_eq(struct proc_status_creds *o1, struct proc_status_creds
{ {
return memcmp(o1, o2, sizeof(struct proc_status_creds)) == 0; return memcmp(o1, o2, sizeof(struct proc_status_creds)) == 0;
} }
int parse_children(pid_t pid, pid_t **_c, int *_n)
{
pid_t *ch = NULL;
int nr = 0;
DIR *dir;
struct dirent *de;
dir = opendir_proc(pid, "task");
if (dir == NULL)
return -1;
while ((de = readdir(dir))) {
int fd, len;
char *pos;
if (dir_dots(de))
continue;
fd = open_proc(pid, "task/%s/children", de->d_name);
if (fd < 0)
goto err;
len = read(fd, buf, BUF_SIZE);
close(fd);
if (len < 0)
goto err;
buf[len] = '\0';
pos = buf;
while (1) {
pid_t val, *tmp;
val = strtol(pos, &pos, 0);
if (!val) {
BUG_ON(*pos != '\0');
break;
}
tmp = xrealloc(ch, (nr + 1) * sizeof(pid_t));
if (!tmp)
goto err;
ch = tmp;
ch[nr] = val;
nr++;
pos++; /* space goes after each pid */
}
}
*_c = ch;
*_n = nr;
closedir(dir);
return 0;
err:
closedir(dir);
xfree(ch);
return -1;
}
#include <stdbool.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <time.h>
#include "compiler.h"
#include "cr_options.h"
#include "pstree.h"
#include "ptrace.h"
#include "seize.h"
#include "stats.h"
#include "xmalloc.h"
#include "util.h"
#define NR_ATTEMPTS 5
static inline bool child_collected(struct pstree_item *i, pid_t pid)
{
struct pstree_item *c;
list_for_each_entry(c, &i->children, sibling)
if (c->pid.real == pid)
return true;
return false;
}
static int collect_task(struct pstree_item *item);
static int collect_children(struct pstree_item *item)
{
pid_t *ch;
int ret, i, nr_children, nr_inprogress;
ret = parse_children(item->pid.real, &ch, &nr_children);
if (ret < 0)
return ret;
nr_inprogress = 0;
for (i = 0; i < nr_children; i++) {
struct pstree_item *c;
pid_t pid = ch[i];
/* Is it already frozen? */
if (child_collected(item, pid))
continue;
nr_inprogress++;
pr_info("Seized task %d, state %d\n", pid, ret);
c = alloc_pstree_item();
if (c == NULL) {
ret = -1;
goto free;
}
ret = seize_task(pid, item->pid.real, &dmpi(c)->pi_creds);
if (ret < 0) {
/*
* Here is a race window between parse_children() and seize(),
* so the task could die for these time.
* Don't worry, will try again on the next attempt. The number
* of attempts is restricted, so it will exit if something
* really wrong.
*/
ret = 0;
xfree(c);
continue;
}
c->pid.real = pid;
c->parent = item;
c->state = ret;
list_add_tail(&c->sibling, &item->children);
/* Here is a recursive call (Depth-first search) */
ret = collect_task(c);
if (ret < 0)
goto free;
}
free:
xfree(ch);
return ret < 0 ? ret : nr_inprogress;
}
static void unseize_task_and_threads(const struct pstree_item *item, int st)
{
int i;
if (item->state == TASK_DEAD)
return;
/*
* The st is the state we want to switch tasks into,
* the item->state is the state task was in when we seized one.
*/
unseize_task(item->pid.real, item->state, st);
if (st == TASK_DEAD)
return;
for (i = 1; i < item->nr_threads; i++)
if (ptrace(PTRACE_DETACH, item->threads[i].real, NULL, NULL))
pr_perror("Unable to detach from %d", item->threads[i].real);
}
static void pstree_wait(struct pstree_item *root_item)
{
struct pstree_item *item = root_item;
int pid, status, i;
for_each_pstree_item(item) {
if (item->state == TASK_DEAD)
continue;
for (i = 0; i < item->nr_threads; i++) {
pid = wait4(-1, &status, __WALL, NULL);
if (pid < 0) {
pr_perror("wait4 failed");
break;
} else {
if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL) {
pr_err("Unexpected exit code %d of %d\n", status, pid);
BUG();
}
}
}
}
pid = wait4(-1, &status, __WALL, NULL);
if (pid > 0) {
pr_err("Unexpected child %d", pid);
BUG();
}
}
void pstree_switch_state(struct pstree_item *root_item, int st)
{
struct pstree_item *item = root_item;
pr_info("Unfreezing tasks into %d\n", st);
for_each_pstree_item(item)
unseize_task_and_threads(item, st);
if (st == TASK_DEAD)
pstree_wait(root_item);
}
static pid_t item_ppid(const struct pstree_item *item)
{
item = item->parent;
return item ? item->pid.real : -1;
}
static inline bool thread_collected(struct pstree_item *i, pid_t tid)
{
int t;
if (i->pid.real == tid) /* thread leader is collected as task */
return true;
for (t = 0; t < i->nr_threads; t++)
if (tid == i->threads[t].real)
return true;
return false;
}
static int collect_threads(struct pstree_item *item)
{
struct pid *threads = NULL;
int nr_threads = 0, i = 0, ret, nr_inprogress, nr_stopped = 0;
ret = parse_threads(item->pid.real, &threads, &nr_threads);
if (ret < 0)
goto err;
if ((item->state == TASK_DEAD) && (nr_threads > 1)) {
pr_err("Zombies with threads are not supported\n");
goto err;
}
/* The number of threads can't be less than allready frozen */
item->threads = xrealloc(item->threads, nr_threads * sizeof(struct pid));
if (item->threads == NULL)
return -1;
if (item->nr_threads == 0) {
item->threads[0].real = item->pid.real;
item->nr_threads = 1;
}
nr_inprogress = 0;
for (i = 0; i < nr_threads; i++) {
pid_t pid = threads[i].real;
if (thread_collected(item, pid))
continue;
nr_inprogress++;
pr_info("\tSeizing %d's %d thread\n",
item->pid.real, pid);
ret = seize_task(pid, item_ppid(item), &dmpi(item)->pi_creds);
if (ret < 0) {
/*
* Here is a race window between parse_threads() and seize(),
* so the task could die for these time.
* Don't worry, will try again on the next attempt. The number
* of attempts is restricted, so it will exit if something
* really wrong.
*/
continue;
}
BUG_ON(item->nr_threads + 1 > nr_threads);
item->threads[item->nr_threads].real = pid;
item->nr_threads++;
if (ret == TASK_DEAD) {
pr_err("Zombie thread not supported\n");
goto err;
}
if (ret == TASK_STOPPED) {
nr_stopped++;
}
}
if (nr_stopped && nr_stopped != nr_inprogress) {
pr_err("Individually stopped threads not supported\n");
goto err;
}
xfree(threads);
return nr_inprogress;
err:
xfree(threads);
return -1;
}
static int collect_loop(struct pstree_item *item,
int (*collect)(struct pstree_item *))
{
int attempts = NR_ATTEMPTS, nr_inprogress = 1;
/*
* While we scan the proc and seize the children/threads
* new ones can appear (with clone(CLONE_PARENT) or with
* pthread_create). Thus, after one go, we need to repeat
* the scan-and-freeze again collecting new arrivals. As
* new guys may appear again we do NR_ATTEMPTS passes and
* fail to seize the item if new tasks/threads still
* appear.
*/
while (nr_inprogress > 0 && attempts) {
attempts--;
nr_inprogress = collect(item);
}
/*
* We may fail to collect items or run out of attempts.
* In the former case nr_inprogress will be negative, in
* the latter -- positive. Thus it's enough just to check
* for "no more new stuff" and say "we're OK" if so.
*/
return (nr_inprogress == 0) ? 0 : -1;
}
static int collect_task(struct pstree_item *item)
{
int ret;
ret = collect_loop(item, collect_threads);
if (ret < 0)
goto err_close;
/* Depth-first search (DFS) is used for traversing a process tree. */
ret = collect_loop(item, collect_children);
if (ret < 0)
goto err_close;
if ((item->state == TASK_DEAD) && !list_empty(&item->children)) {
pr_err("Zombie with children?! O_o Run, run, run!\n");
goto err_close;
}
if (pstree_alloc_cores(item))
goto err_close;
pr_info("Collected %d in %d state\n", item->pid.real, item->state);
return 0;
err_close:
close_pid_proc();
return -1;
}
int collect_pstree(pid_t pid)
{
int ret;
timing_start(TIME_FREEZING);
root_item = alloc_pstree_item();
if (root_item == NULL)
return -1;
root_item->pid.real = pid;
ret = seize_task(pid, -1, &dmpi(root_item)->pi_creds);
if (ret < 0)
goto err;
pr_info("Seized task %d, state %d\n", pid, ret);
root_item->state = ret;
ret = collect_task(root_item);
if (ret < 0)
goto err;
timing_stop(TIME_FREEZING);
timing_start(TIME_FROZEN);
return 0;
err:
pstree_switch_state(root_item, TASK_ALIVE);
return -1;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment