[patch] Break by Ctrl-C before first syscall SIGSTOPs the traced process

Jan Kratochvil jan.kratochvil at redhat.com
Wed May 23 16:30:23 UTC 2007


Hi,

CTRL-C of `strace -p' attached to a process before it is able to print the first
syscall will get the application Stopped (T, by SIGSTOP) and needs to be sent
`kill -CONT'.  Shell prints:
[1]+  Stopped                 appname args

1. Run "mke2fs /dev/sda2" or "mke2fs -F HUGE" in one terminal.
2. In another terminal, attach to the process with
      strace -p `/sbin/pidof /sbin/mke2fs`
   at the moment when the mke2fs process has just written
      "Writing superblocks and filesystem accounting information:"
3. Hit Ctrl-C to break the strace.

Each PTRACE_ATTACH generates one SIGSTOP.  We need to catch the SIGSTOP by
waitpid () as if we terminate before catching it the traced process will
receive the artifical SIGSTOP and stop itself.  Be aware the other signals may
be received before SIGSTOP and these need to be redelivered.
[ Roland McGrath originally provided this useful info. ]

I did not specifically analyse where in the current code the generated SIGSTOPs
get caught as they are not shown to the user.


Regards,
Jan

Problem tracked at:
	https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=240986
-------------- next part --------------
2007-05-23  Jan Kratochvil  <jan.kratochvil at redhat.com>

	* strace.c [LINUX] (detach): Move the SIGSTOP wait () code with its
	STATUS variable to ...
	[LINUX] (waitstop): ... here as a new function.
	(main): Move signals and BLOCKED_SET init before the options parsing.
	[LINUX] (main): waitstop () once after each succeeded PTRACE_ATTACH.
	SIG_BLOCK signals of BLOCKED_SET between PTRACE_ATTACH - waitstop ().

diff -u -rup strace-4.5.15-detach-zombie-tkill/strace.c strace-4.5.15/strace.c
--- strace-4.5.15-detach-zombie-tkill/strace.c	2007-05-23 16:32:55.000000000 +0200
+++ strace-4.5.15/strace.c	2007-05-23 17:27:44.000000000 +0200
@@ -325,6 +325,67 @@ newoutf(struct tcb *tcp)
 	return 0;
 }
 
+#ifdef LINUX
+
+/* Each PTRACE_ATTACH generates one SIGSTOP.  We need to catch it as it would
+   mess our later tracing otherwise.  Be aware the other signals may be
+   received before SIGSTOP and these need to be redelivered.
+   Function returns 1 on successful caught SIGSTOP, 0 died PID, -1 on error.  */
+static int waitstop P((int pid));
+static int
+waitstop(pid)
+int pid;
+{
+	int status;
+
+retry:
+#ifdef __WALL
+	if (wait4(pid, &status, __WALL, NULL) < 0) {
+		if (errno == ECHILD) /* Already gone.  */
+			return 0;
+		if (errno != EINVAL) {
+			perror("waitstop: waiting");
+			return -1;
+		}
+#endif /* __WALL */
+		/* No __WALL here.  */
+		if (waitpid(pid, &status, 0) < 0) {
+			if (errno != ECHILD) {
+				perror("waitstop: waiting");
+				return -1;
+			}
+#ifdef __WCLONE
+			/* If no processes, try clones.  */
+			if (wait4(pid, &status, __WCLONE, NULL) < 0) {
+				if (errno == ECHILD)
+					return 0;
+				perror("waitstop: waiting");
+				return -1;
+			}
+#endif /* __WCLONE */
+		}
+#ifdef __WALL
+	}
+#endif
+	if (!WIFSTOPPED(status)) {
+		/* Au revoir, mon ami. */
+		return 0;
+	}
+	if (WSTOPSIG(status) == SIGSTOP) {
+		return 1;
+	}
+	if (ptrace(PTRACE_CONT, pid, (char *) 1, WSTOPSIG(status) == SIGTRAP
+						 ?  0 : WSTOPSIG(status)) < 0) {
+		if (errno == ESRCH)
+			return 0;
+		perror("waitstop: ptrace(PTRACE_CONT, ...)");
+		return -1;
+	}
+	goto retry;
+}
+
+#endif /* LINUX */
+
 int
 main(argc, argv)
 int argc;
@@ -523,6 +584,43 @@ char *argv[];
 		qflag = 1;
 	}
 
+	sigemptyset(&empty_set);
+	sigemptyset(&blocked_set);
+	sa.sa_handler = SIG_IGN;
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = 0;
+	sigaction(SIGTTOU, &sa, NULL);
+	sigaction(SIGTTIN, &sa, NULL);
+	if (interactive) {
+		sigaddset(&blocked_set, SIGHUP);
+		sigaddset(&blocked_set, SIGINT);
+		sigaddset(&blocked_set, SIGQUIT);
+		sigaddset(&blocked_set, SIGPIPE);
+		sigaddset(&blocked_set, SIGTERM);
+		sa.sa_handler = interrupt;
+#ifdef SUNOS4
+		/* POSIX signals on sunos4.1 are a little broken. */
+		sa.sa_flags = SA_INTERRUPT;
+#endif /* SUNOS4 */
+	}
+	sigaction(SIGHUP, &sa, NULL);
+	sigaction(SIGINT, &sa, NULL);
+	sigaction(SIGQUIT, &sa, NULL);
+	sigaction(SIGPIPE, &sa, NULL);
+	sigaction(SIGTERM, &sa, NULL);
+#ifdef USE_PROCFS
+	sa.sa_handler = reaper;
+	sigaction(SIGCHLD, &sa, NULL);
+#else
+	/* Make sure SIGCHLD has the default action so that waitpid
+	   definitely works without losing track of children.  The user
+	   should not have given us a bogus state to inherit, but he might
+	   have.  Arguably we should detect SIG_IGN here and pass it on
+	   to children, but probably noone really needs that.  */
+	sa.sa_handler = SIG_DFL;
+	sigaction(SIGCHLD, &sa, NULL);
+#endif /* USE_PROCFS */
+
 	for (c = 0; c < tcbtabsize; c++) {
 		tcp = tcbtab[c];
 		if (!(tcp->flags & TCB_INUSE) || !(tcp->flags & TCB_ATTACHED))
@@ -562,10 +660,16 @@ char *argv[];
 					if (tid <= 0)
 						continue;
 					++ntid;
+					/* We would leave the process SIGSTOPped if we would get terminated
+					   between PTRACE_ATTACH and waitstop() catching the SIGSTOP.  */
+					sigprocmask(SIG_BLOCK, &blocked_set, NULL);
 					if (ptrace(PTRACE_ATTACH, tid,
 						   (char *) 1, 0) < 0)
 						++nerr;
-					else if (tid != tcbtab[c]->pid) {
+					else if (waitstop (tid) <= 0) {
+						fprintf (stderr, "Error while attaching to tid %d!\n", tcp->pid);
+						++nerr;
+					} else if (tid != tcbtab[c]->pid) {
 						if (nprocs == tcbtabsize &&
 						    expand_tcbtab())
 							tcp = NULL;
@@ -579,6 +683,11 @@ char *argv[];
 						tcbtab[c]->nclone_detached++;
 						tcp->parent = tcbtab[c];
 					}
+					sigprocmask(SIG_SETMASK, &empty_set, NULL);
+					if (interrupted) {
+						cleanup();
+						exit(0);
+					}
 				}
 				closedir(dir);
 				if (nerr == ntid) {
@@ -601,11 +710,25 @@ Process %u attached - interrupt to quit\
 			}
 		}
 # endif
+		/* We would leave the process SIGSTOPped if we would get terminated
+		   between PTRACE_ATTACH and waitstop() catching the SIGSTOP.  */
+		sigprocmask(SIG_BLOCK, &blocked_set, NULL);
 		if (ptrace(PTRACE_ATTACH, tcp->pid, (char *) 1, 0) < 0) {
 			perror("attach: ptrace(PTRACE_ATTACH, ...)");
 			droptcb(tcp);
-			continue;
+			tcp = NULL;
+		} else if (waitstop (tcp->pid) <= 0) {
+			fprintf (stderr, "Error while attaching to pid %d!\n", tcp->pid);
+			detach(tcp, 0);
+			tcp = NULL;
+		}
+		sigprocmask(SIG_SETMASK, &empty_set, NULL);
+		if (interrupted) {
+			cleanup();
+			exit(0);
 		}
+		if (tcp == NULL)
+			continue;
 #endif /* !USE_PROCFS */
 		if (!qflag)
 			fprintf(stderr,
@@ -767,43 +890,6 @@ Process %u attached - interrupt to quit\
 		}
 	}
 
-	sigemptyset(&empty_set);
-	sigemptyset(&blocked_set);
-	sa.sa_handler = SIG_IGN;
-	sigemptyset(&sa.sa_mask);
-	sa.sa_flags = 0;
-	sigaction(SIGTTOU, &sa, NULL);
-	sigaction(SIGTTIN, &sa, NULL);
-	if (interactive) {
-		sigaddset(&blocked_set, SIGHUP);
-		sigaddset(&blocked_set, SIGINT);
-		sigaddset(&blocked_set, SIGQUIT);
-		sigaddset(&blocked_set, SIGPIPE);
-		sigaddset(&blocked_set, SIGTERM);
-		sa.sa_handler = interrupt;
-#ifdef SUNOS4
-		/* POSIX signals on sunos4.1 are a little broken. */
-		sa.sa_flags = SA_INTERRUPT;
-#endif /* SUNOS4 */
-	}
-	sigaction(SIGHUP, &sa, NULL);
-	sigaction(SIGINT, &sa, NULL);
-	sigaction(SIGQUIT, &sa, NULL);
-	sigaction(SIGPIPE, &sa, NULL);
-	sigaction(SIGTERM, &sa, NULL);
-#ifdef USE_PROCFS
-	sa.sa_handler = reaper;
-	sigaction(SIGCHLD, &sa, NULL);
-#else
-	/* Make sure SIGCHLD has the default action so that waitpid
-	   definitely works without losing track of children.  The user
-	   should not have given us a bogus state to inherit, but he might
-	   have.  Arguably we should detect SIG_IGN here and pass it on
-	   to children, but probably noone really needs that.  */
-	sa.sa_handler = SIG_DFL;
-	sigaction(SIGCHLD, &sa, NULL);
-#endif /* USE_PROCFS */
-
 	if (trace() < 0)
 		exit(1);
 	cleanup();
@@ -1297,7 +1383,7 @@ int sig;
 {
 	int error = 0;
 #ifdef LINUX
-	int status, resumed;
+	int resumed;
 	struct tcb *zombie = NULL;
 
 	/* If the group leader is lingering only because of this other
@@ -1340,54 +1426,14 @@ int sig;
 			perror("detach: stopping child");
 	}
 	else {
-		for (;;) {
-#ifdef __WALL
-			if (wait4(tcp->pid, &status, __WALL, NULL) < 0) {
-				if (errno == ECHILD) /* Already gone.  */
-					break;
-				if (errno != EINVAL) {
-					perror("detach: waiting");
-					break;
-				}
-#endif /* __WALL */
-				/* No __WALL here.  */
-				if (waitpid(tcp->pid, &status, 0) < 0) {
-					if (errno != ECHILD) {
-						perror("detach: waiting");
-						break;
-					}
-#ifdef __WCLONE
-					/* If no processes, try clones.  */
-					if (wait4(tcp->pid, &status, __WCLONE,
-						  NULL) < 0) {
-						if (errno != ECHILD)
-							perror("detach: waiting");
-						break;
-					}
-#endif /* __WCLONE */
-				}
-#ifdef __WALL
-			}
-#endif
-			if (!WIFSTOPPED(status)) {
-				/* Au revoir, mon ami. */
-				break;
-			}
-			if (WSTOPSIG(status) == SIGSTOP) {
-				if ((error = ptrace(PTRACE_DETACH,
-				    tcp->pid, (char *) 1, sig)) < 0) {
-					if (errno != ESRCH)
-						perror("detach: ptrace(PTRACE_DETACH, ...)");
-					/* I died trying. */
-				}
-				break;
-			}
-			if ((error = ptrace(PTRACE_CONT, tcp->pid, (char *) 1,
-			    WSTOPSIG(status) == SIGTRAP ?
-			    0 : WSTOPSIG(status))) < 0) {
+		int waitstop_result = waitstop (tcp->pid);
+		error = waitstop_result < 0;
+		if (waitstop_result > 0) {
+			if ((error = ptrace(PTRACE_DETACH, tcp->pid, (char *) 1,
+					    sig)) < 0) {
 				if (errno != ESRCH)
-					perror("detach: ptrace(PTRACE_CONT, ...)");
-				break;
+					perror("detach: ptrace(PTRACE_DETACH, ...)");
+				/* I died trying. */
 			}
 		}
 	}


More information about the Strace-devel mailing list