Introduce xxx_utrace_stop() which notifies engine we are going to stop. It calls report_quiesce(0), but report.action = UTRACE_STOP instead of UTRACE_RESUME.
Change ptrace_stop() to use xxx_utrace_stop(). Yes, it is racy and can stop after detach, but afaics this is fixeable when utrace_stop() will be fixed. Change utrace_stop() to use another ugly hack, xxx_ptrace_notify_stop(), to notify the parent. --- include/linux/sched.h | 1 + include/linux/utrace.h | 3 +++ include/linux/ptrace.h | 4 ++++ kernel/utrace.c | 20 ++++++++++++++++++++ kernel/ptrace.c | 36 ++++++++++++++++++++++++++++++++++++ kernel/signal.c | 24 +++--------------------- 6 files changed, 67 insertions(+), 21 deletions(-) --- MINI/include/linux/sched.h~3_PTRACE_NOTIFY 2009-08-25 17:00:35.000000000 +0200 +++ MINI/include/linux/sched.h 2009-08-25 17:11:38.000000000 +0200 @@ -1980,6 +1980,7 @@ extern int kill_pgrp(struct pid *pid, in extern int kill_pid(struct pid *pid, int sig, int priv); extern int kill_proc_info(int, struct siginfo *, pid_t); extern int do_notify_parent(struct task_struct *, int); +extern void do_notify_parent_cldstop(struct task_struct *, int); extern void force_sig(int, struct task_struct *); extern void force_sig_specific(int, struct task_struct *); extern int send_sig(int, struct task_struct *, int); --- MINI/include/linux/utrace.h~3_PTRACE_NOTIFY 2009-08-17 11:56:56.000000000 +0200 +++ MINI/include/linux/utrace.h 2009-08-25 17:20:24.000000000 +0200 @@ -624,6 +624,9 @@ int __must_check utrace_finish_examine(s struct utrace_engine *, struct utrace_examiner *); +void xxx_utrace_stop(void); + + /** * utrace_control_pid - control a thread being traced by a tracing engine * @pid: thread to affect --- MINI/include/linux/ptrace.h~3_PTRACE_NOTIFY 2009-08-25 17:00:35.000000000 +0200 +++ MINI/include/linux/ptrace.h 2009-08-25 17:35:45.000000000 +0200 @@ -94,6 +94,10 @@ extern void __ptrace_link(struct task_st struct task_struct *new_parent); extern void __ptrace_unlink(struct task_struct *child); extern void exit_ptrace(struct task_struct *tracer); + +extern void ptrace_do_stop(void); +void xxx_ptrace_notify_stop(struct task_struct *); + #define PTRACE_MODE_READ 1 #define PTRACE_MODE_ATTACH 2 /* Returns 0 on success, -errno on denial. */ --- MINI/kernel/utrace.c~3_PTRACE_NOTIFY 2009-08-25 12:33:17.000000000 +0200 +++ MINI/kernel/utrace.c 2009-08-25 17:33:51.000000000 +0200 @@ -398,6 +398,9 @@ static void utrace_stop(struct task_stru spin_unlock_irq(&task->sighand->siglock); spin_unlock(&utrace->lock); + if (task_ptrace(task)) + xxx_ptrace_notify_stop(task); + schedule(); /* @@ -1819,6 +1822,23 @@ void utrace_resume(struct task_struct *t finish_resume_report(&report, task, utrace); } +void xxx_utrace_stop(void) +{ + struct task_struct *task = current; + struct utrace *utrace = task_utrace_struct(task); + struct utrace_engine *engine; + INIT_REPORT(report); + + report.action = UTRACE_STOP; + + start_report(utrace); + + list_for_each_entry(engine, &utrace->attached, entry) + start_callback(utrace, &report, engine, task, 0); + + finish_resume_report(&report, task, utrace); +} + /* * Return true if current has forced signal_pending(). * --- MINI/kernel/ptrace.c~3_PTRACE_NOTIFY 2009-08-25 17:00:35.000000000 +0200 +++ MINI/kernel/ptrace.c 2009-08-25 17:41:47.000000000 +0200 @@ -62,6 +62,42 @@ static int ptrace_attach_task(struct tas return 0; } +void ptrace_do_stop(void) +{ + struct utrace_engine *engine; + + engine = utrace_attach_task(current, UTRACE_ATTACH_MATCH_OPS, + &ptrace_utrace_ops, NULL); + if (unlikely(IS_ERR(engine))) + return; + + utrace_control(current, engine, UTRACE_STOP); /* unneeded curently */ + + engine->data = (void*)1; /* marker for xxx_ptrace_notify_stop() */ + xxx_utrace_stop(); + engine->data = NULL; + + utrace_engine_put(engine); +} + +void xxx_ptrace_notify_stop(struct task_struct *task) +{ + struct utrace_engine *engine; + + engine = utrace_attach_task(task, UTRACE_ATTACH_MATCH_OPS, + &ptrace_utrace_ops, NULL); + if (unlikely(IS_ERR(engine))) + return; + + if (engine->data) { + read_lock(&tasklist_lock); + do_notify_parent_cldstop(task, CLD_TRAPPED); + read_unlock(&tasklist_lock); + } + + utrace_engine_put(engine); +} + /* * ptrace a task: make the debugger its new parent and * move it to the ptrace list. --- MINI/kernel/signal.c~3_PTRACE_NOTIFY 2009-08-25 17:00:35.000000000 +0200 +++ MINI/kernel/signal.c 2009-08-25 17:11:38.000000000 +0200 @@ -1479,7 +1479,7 @@ int do_notify_parent(struct task_struct return ret; } -static void do_notify_parent_cldstop(struct task_struct *tsk, int why) +void do_notify_parent_cldstop(struct task_struct *tsk, int why) { struct siginfo info; unsigned long flags; @@ -1595,32 +1595,14 @@ static void ptrace_stop(int exit_code, i return; } - /* - * If there is a group stop in progress, - * we must participate in the bookkeeping. - */ - if (current->signal->group_stop_count > 0) - --current->signal->group_stop_count; - current->last_siginfo = info; current->exit_code = exit_code; - - /* Let the debugger run. */ - __set_current_state(TASK_TRACED); spin_unlock_irq(¤t->sighand->siglock); read_lock(&tasklist_lock); if (may_ptrace_stop()) { - do_notify_parent_cldstop(current, CLD_TRAPPED); - /* - * Don't want to allow preemption here, because - * sys_ptrace() needs this task to be inactive. - * - * XXX: implement read_unlock_no_resched(). - */ - preempt_disable(); read_unlock(&tasklist_lock); - preempt_enable_no_resched(); - schedule(); + + ptrace_do_stop(); } else { /* * By the time we got the lock, our tracer went away.