Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 6 Feb 2012 13:19:30 -0800
From:      Dmitry Mikulin <dmitrym@juniper.net>
To:        Konstantin Belousov <kostikbel@gmail.com>
Cc:        freebsd-current Current <freebsd-current@freebsd.org>, Marcel Moolenaar <marcelm@juniper.net>
Subject:   Re: [ptrace] please review follow fork/exec changes
Message-ID:  <4F3043E2.6090607@juniper.net>
In-Reply-To: <20120204204218.GC3283@deviant.kiev.zoral.com.ua>
References:  <749E238A-A85F-4264-9DEB-BCE1BBD21C9D@juniper.net> <20120125074824.GD2726@deviant.kiev.zoral.com.ua> <4F2094B4.70707@juniper.net> <20120126122326.GT2726@deviant.kiev.zoral.com.ua> <4F22E8FD.6010201@juniper.net> <20120129074843.GL2726@deviant.kiev.zoral.com.ua> <4F26E0D1.8040100@juniper.net> <20120130192727.GZ2726@deviant.kiev.zoral.com.ua> <4F2C756A.80900@juniper.net> <20120204204218.GC3283@deviant.kiev.zoral.com.ua>

next in thread | previous in thread | raw e-mail | index | archive | help
--------------000108040008060709070606
Content-Type: text/plain; charset="ISO-8859-1"; format=flowed
Content-Transfer-Encoding: 7bit


> I see what is going on. The wait loop for P_PPWAIT in do_fork() simply
> do not allow the ptracestop() in the syscall return path to be reached.
> There seems to be more problems. In particular, I do not see anything
> which would prevent the child from being reapped while the loop is
> executing (assume that the parent is multithreaded and other thread
> processed SIGCHLD and called wait).
>
> Lets deal with these bugs after your proposal for interface changes is
> dealt with.

OK.

>
> Yes, I agree with the proposal to add flag to the child lwp info.
> I think it will be easier if the flag is different from PL_FLAG_FORKED.
> I named it PL_FLAG_CHILD.
>
> PT_FOLLOW_EXEC is easy to implement, but my question is, how can debugger
> operate (correctly) if it ignores exec events ? After exec, the whole
> cached state of the debuggee must be invalidated, and since debugger
> ignores the notification when the invalidation shall be done, it probably
> gets very confused.

You're right, the debugger needs to handle exec() events implicitly when it starts up executables. The problem is that there is OS-independent machinery in gdb which handles statup fork-exec sequence differently from when the debuggee itself does an exec(). Basically in the event handling code I need to be able to distinguish app startup by gdb from an exec done by the app. Other OS-es have flags like PL_FLAG_EXEC set on demand: they have an equivalent of PT_FOLLOW_EXEC. I attached a modified patch that solves the problem. It tries to separate the always-on TDB_EXEC from the on-demand TDB_FOLLOWEXEC without changing existing functionality. Let me know if it's acceptable.

Another issue I'm investigating is that after the switch-over to the child gdb gets a SIGHUP when it continues the child. I think it has to do with the re-parenting/orphan business. I'll let you know what I find, but if you have an idea what might be causing it, please let me know.

Thanks.
Dmitry.




--------------000108040008060709070606
Content-Type: text/x-patch; name="follow-exec-2.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="follow-exec-2.diff"

Index: kern/kern_exec.c
===================================================================
--- kern/kern_exec.c	(revision 231088)
+++ kern/kern_exec.c	(working copy)
@@ -890,6 +890,8 @@ exec_fail_dealloc:
 	if (error == 0) {
 		PROC_LOCK(p);
 		td->td_dbgflags |= TDB_EXEC;
+		if (p->p_flag & P_FOLLOWEXEC)
+			td->td_dbgflags |= TDB_FOLLOWEXEC;
 		PROC_UNLOCK(p);
 
 		/*
Index: kern/kern_fork.c
===================================================================
--- kern/kern_fork.c	(revision 231088)
+++ kern/kern_fork.c	(working copy)
@@ -1035,7 +1035,9 @@ fork_return(struct thread *td, struct trapframe *f
 			p->p_oppid = p->p_pptr->p_pid;
 			proc_reparent(p, dbg);
 			sx_xunlock(&proctree_lock);
+			td->td_dbgflags |= TDB_CHILD;
 			ptracestop(td, SIGSTOP);
+			td->td_dbgflags &= ~TDB_CHILD;
 		} else {
 			/*
 			 * ... otherwise clear the request.
Index: kern/sys_process.c
===================================================================
--- kern/sys_process.c	(revision 231088)
+++ kern/sys_process.c	(working copy)
@@ -660,6 +660,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid,
 	case PT_TO_SCX:
 	case PT_SYSCALL:
 	case PT_FOLLOW_FORK:
+	case PT_FOLLOW_EXEC:
 	case PT_DETACH:
 		sx_xlock(&proctree_lock);
 		proctree_locked = 1;
@@ -873,6 +874,12 @@ kern_ptrace(struct thread *td, int req, pid_t pid,
 		else
 			p->p_flag &= ~P_FOLLOWFORK;
 		break;
+	case PT_FOLLOW_EXEC:
+		if (data)
+			p->p_flag &= ~P_FOLLOWEXEC;
+		else
+			p->p_flag |= P_FOLLOWEXEC;
+		break;
 
 	case PT_STEP:
 	case PT_CONTINUE:
@@ -936,7 +943,8 @@ kern_ptrace(struct thread *td, int req, pid_t pid,
 					p->p_sigparent = SIGCHLD;
 			}
 			p->p_oppid = 0;
-			p->p_flag &= ~(P_TRACED | P_WAITED | P_FOLLOWFORK);
+			p->p_flag &= ~(P_TRACED | P_WAITED | P_FOLLOWFORK |
+			    P_FOLLOWEXEC);
 
 			/* should we send SIGCHLD? */
 			/* childproc_continued(p); */
@@ -1141,10 +1149,14 @@ kern_ptrace(struct thread *td, int req, pid_t pid,
 			pl->pl_flags |= PL_FLAG_SCX;
 		if (td2->td_dbgflags & TDB_EXEC)
 			pl->pl_flags |= PL_FLAG_EXEC;
+		if (td2->td_dbgflags & TDB_FOLLOWEXEC)
+			pl->pl_flags |= PL_FLAG_FOLLOWEXEC;
 		if (td2->td_dbgflags & TDB_FORK) {
 			pl->pl_flags |= PL_FLAG_FORKED;
 			pl->pl_child_pid = td2->td_dbg_forked;
 		}
+		if (td2->td_dbgflags & TDB_CHILD)
+			pl->pl_flags |= PL_FLAG_CHILD;
 		pl->pl_sigmask = td2->td_sigmask;
 		pl->pl_siglist = td2->td_siglist;
 		strcpy(pl->pl_tdname, td2->td_name);
Index: kern/subr_syscall.c
===================================================================
--- kern/subr_syscall.c	(revision 230847)
+++ kern/subr_syscall.c	(working copy)
@@ -216,7 +216,8 @@ syscallret(struct thread *td, int error, struct sy
 		    ((td->td_dbgflags & (TDB_FORK | TDB_EXEC)) != 0 ||
 		    (p->p_stops & S_PT_SCX) != 0))
 			ptracestop(td, SIGTRAP);
-		td->td_dbgflags &= ~(TDB_SCX | TDB_EXEC | TDB_FORK);
+		td->td_dbgflags &= 
+			~(TDB_SCX | TDB_EXEC | TDB_FOLLOWEXEC | TDB_FORK);
 		PROC_UNLOCK(p);
 	}
 }
Index: sys/proc.h
===================================================================
--- sys/proc.h	(revision 231088)
+++ sys/proc.h	(working copy)
@@ -384,6 +384,8 @@ do {									\
 				      process */
 #define	TDB_STOPATFORK	0x00000080 /* Stop at the return from fork (child
 				      only) */
+#define	TDB_CHILD	0x00000100 /* New child indicator for ptrace() */
+#define	TDB_FOLLOWEXEC	0x00000200 /* follow exec(2) */
 
 /*
  * "Private" flags kept in td_pflags:
@@ -613,6 +615,7 @@ struct proc {
 #define	P_HWPMC		0x800000 /* Process is using HWPMCs */
 
 #define	P_JAILED	0x1000000 /* Process is in jail. */
+#define	P_FOLLOWEXEC	0x2000000 /* Do not report execs with ptrace. */
 #define	P_INEXEC	0x4000000 /* Process is in execve(). */
 #define	P_STATCHILD	0x8000000 /* Child process stopped or exited. */
 #define	P_INMEM		0x10000000 /* Loaded into memory. */
Index: sys/ptrace.h
===================================================================
--- sys/ptrace.h	(revision 231088)
+++ sys/ptrace.h	(working copy)
@@ -64,6 +64,7 @@
 #define	PT_SYSCALL	22
 
 #define	PT_FOLLOW_FORK	23
+#define	PT_FOLLOW_EXEC	24
 
 #define PT_GETREGS      33	/* get general-purpose registers */
 #define PT_SETREGS      34	/* set general-purpose registers */
@@ -106,7 +107,9 @@ struct ptrace_lwpinfo {
 #define	PL_FLAG_SCX	0x08	/* syscall leave point */
 #define	PL_FLAG_EXEC	0x10	/* exec(2) succeeded */
 #define	PL_FLAG_SI	0x20	/* siginfo is valid */
-#define	PL_FLAG_FORKED	0x40	/* new child */
+#define	PL_FLAG_FORKED	0x40	/* child born */
+#define	PL_FLAG_CHILD	0x80	/* I am from child */
+#define	PL_FLAG_FOLLOWEXEC	0x100	/* follow exec(2) */
 	sigset_t	pl_sigmask;	/* LWP signal mask */
 	sigset_t	pl_siglist;	/* LWP pending signal */
 	struct __siginfo pl_siginfo;	/* siginfo for signal */

--------------000108040008060709070606--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4F3043E2.6090607>