Blame SOURCES/rhbz1118070.patch

c1bac6
commit b9b055e2f6e7db5197fe90c4b95ae52dd84a5994 (HEAD, TMP)
c1bac6
Author: David Smith <dsmith@redhat.com>
c1bac6
Date:   Fri Jul 18 15:49:39 2014 -0500
c1bac6
c1bac6
    Fixed PR17181 by making utrace handle interrupting processes better.
c1bac6
    
c1bac6
    * runtime/stp_utrace.c (utrace_init): Use kallsyms_lookup_name() to lookup
c1bac6
      "wake_up_state" if needed.
c1bac6
      (stp_task_notify_resume): New function to handle the details of adding a
c1bac6
      resume handler.
c1bac6
      (utrace_cleanup): Only output debug printk's if STP_TF_DEBUG is defined.
c1bac6
      (utrace_set_events): Improve check.
c1bac6
      (utrace_do_stop): Call stp_task_notify_resume() instead of inline code.
c1bac6
      (utrace_wakeup): Call stp_wake_up_state() instead of wake_up_process()
c1bac6
      to avoid a WARN(). Call stp_task_notify_resume() instead of inline
c1bac6
      code.
c1bac6
      (utrace_control): Call stp_task_notify_resume() instead of inline code.
c1bac6
      (finish_report): Ditto.
c1bac6
      (finish_resume_report): Add UTRACE_INTERRUPT support.
c1bac6
      (utrace_resume): Handle UTRACE_INTERRUPT.
c1bac6
    * runtime/linux/task_finder2.c (stap_task_finder_post_init): Go back to
c1bac6
      sending UTRACE_INTERRUPT to all tasks.
c1bac6
    * buildrun.cxx (compile_pass): Add export tests for 'wake_up_state' and
c1bac6
      'try_to_wake_up'.
c1bac6
    * runtime/linux/runtime.h: Added 'kallsyms_wake_up_state' declaration when
c1bac6
      necessary.
c1bac6
    * testsuite/systemtap.base/process_resume.c: New file.
c1bac6
    * testsuite/systemtap.base/process_resume.exp: New file.
c1bac6
c1bac6
diff --git a/buildrun.cxx b/buildrun.cxx
c1bac6
index a2f2197e4609..87b946a27621 100644
c1bac6
--- a/buildrun.cxx
c1bac6
+++ b/buildrun.cxx
c1bac6
@@ -404,6 +404,8 @@ compile_pass (systemtap_session& s)
c1bac6
 
c1bac6
   // used by runtime/stp_utrace.c
c1bac6
   output_exportconf(s, o, "task_work_add", "STAPCONF_TASK_WORK_ADD_EXPORTED");
c1bac6
+  output_exportconf(s, o, "wake_up_state", "STAPCONF_WAKE_UP_STATE_EXPORTED");
c1bac6
+  output_exportconf(s, o, "try_to_wake_up", "STAPCONF_TRY_TO_WAKE_UP_EXPORTED");
c1bac6
   output_exportconf(s, o, "signal_wake_up_state", "STAPCONF_SIGNAL_WAKE_UP_STATE_EXPORTED");
c1bac6
   output_exportconf(s, o, "signal_wake_up", "STAPCONF_SIGNAL_WAKE_UP_EXPORTED");
c1bac6
   output_exportconf(s, o, "__lock_task_sighand", "STAPCONF___LOCK_TASK_SIGHAND_EXPORTED");
c1bac6
diff --git a/runtime/linux/runtime.h b/runtime/linux/runtime.h
c1bac6
index 76dbea4fd24d..281ce41af72e 100644
c1bac6
--- a/runtime/linux/runtime.h
c1bac6
+++ b/runtime/linux/runtime.h
c1bac6
@@ -180,6 +180,9 @@ static void *kallsyms_task_work_add;
c1bac6
 static void *kallsyms_task_work_cancel;
c1bac6
 #endif
c1bac6
 
c1bac6
+#if !defined(STAPCONF_TRY_TO_WAKE_UP_EXPORTED) && !defined(STAPCONF_WAKE_UP_STATE_EXPORTED)
c1bac6
+static void *kallsyms_wake_up_state;
c1bac6
+#endif
c1bac6
 #if !defined(STAPCONF_SIGNAL_WAKE_UP_STATE_EXPORTED)
c1bac6
 static void *kallsyms_signal_wake_up_state;
c1bac6
 #endif
c1bac6
diff --git a/runtime/linux/task_finder2.c b/runtime/linux/task_finder2.c
c1bac6
index e88543a81039..64f263319c0c 100644
c1bac6
--- a/runtime/linux/task_finder2.c
c1bac6
+++ b/runtime/linux/task_finder2.c
c1bac6
@@ -1805,10 +1805,12 @@ stap_task_finder_post_init(void)
c1bac6
 				int rc = utrace_control(tsk, engine,
c1bac6
 							UTRACE_INTERRUPT);
c1bac6
 				/* If utrace_control() returns
c1bac6
-				 * EINPROGRESS when we're trying to
c1bac6
-				 * stop/interrupt, that means the task
c1bac6
-				 * hasn't stopped quite yet, but will
c1bac6
-				 * soon.  Ignore this error. */
c1bac6
+				 * EINPROGRESS when we're
c1bac6
+				 * trying to stop/interrupt,
c1bac6
+				 * that means the task hasn't
c1bac6
+				 * stopped quite yet, but will
c1bac6
+				 * soon.  Ignore this
c1bac6
+				 * error. */
c1bac6
 				if (rc != 0 && rc != -EINPROGRESS) {
c1bac6
 					_stp_error("utrace_control returned error %d on pid %d",
c1bac6
 						   rc, (int)tsk->pid);
c1bac6
diff --git a/runtime/stp_utrace.c b/runtime/stp_utrace.c
c1bac6
index 0c9d0eba3969..acbe9366ed83 100644
c1bac6
--- a/runtime/stp_utrace.c
c1bac6
+++ b/runtime/stp_utrace.c
c1bac6
@@ -109,6 +109,22 @@ static void utrace_report_exec(void *cb_data __attribute__ ((unused)),
c1bac6
 #define __UTRACE_REGISTERED	1
c1bac6
 static atomic_t utrace_state = ATOMIC_INIT(__UTRACE_UNREGISTERED);
c1bac6
 
c1bac6
+// If wake_up_state() is exported, use it.
c1bac6
+#if defined(STAPCONF_WAKE_UP_STATE_EXPORTED)
c1bac6
+#define stp_wake_up_state wake_up_state
c1bac6
+// Otherwise, try to use try_to_wake_up(). The wake_up_state()
c1bac6
+// function is just a wrapper around try_to_wake_up().
c1bac6
+#elif defined(STAPCONF_TRY_TO_WAKE_UP_EXPORTED)
c1bac6
+static inline int stp_wake_up_state(struct task_struct *p, unsigned int state)
c1bac6
+{
c1bac6
+	return try_to_wake_up(p, state, 0);
c1bac6
+}
c1bac6
+// Otherwise, we'll have to look up wake_up_state() with kallsyms.
c1bac6
+#else
c1bac6
+typedef typeof(&wake_up_state) wake_up_state_fn;
c1bac6
+#define stp_wake_up_state (* (wake_up_state_fn)kallsyms_wake_up_state)
c1bac6
+#endif
c1bac6
+
c1bac6
 #if !defined(STAPCONF_SIGNAL_WAKE_UP_STATE_EXPORTED)
c1bac6
 // Sigh. On kernel's without signal_wake_up_state(), there is no
c1bac6
 // declaration to use in 'typeof(&signal_wake_up_state)'. So, we'll
c1bac6
@@ -188,6 +204,14 @@ static int utrace_init(void)
c1bac6
 		INIT_HLIST_HEAD(&task_utrace_table[i]);
c1bac6
 	}
c1bac6
 
c1bac6
+#if !defined(STAPCONF_TRY_TO_WAKE_UP_EXPORTED) \
c1bac6
+    && !defined(STAPCONF_WAKE_UP_STATE_EXPORTED)
c1bac6
+	kallsyms_wake_up_state = (void *)kallsyms_lookup_name("wake_up_state");
c1bac6
+        if (kallsyms_wake_up_state == NULL) {
c1bac6
+		_stp_error("Can't resolve wake_up_state!");
c1bac6
+		goto error;
c1bac6
+        }
c1bac6
+#endif
c1bac6
 #if !defined(STAPCONF_SIGNAL_WAKE_UP_STATE_EXPORTED)
c1bac6
 	/* The signal_wake_up_state() function (which replaces
c1bac6
 	 * signal_wake_up() in newer kernels) isn't exported. Look up
c1bac6
@@ -316,6 +340,29 @@ static int utrace_exit(void)
c1bac6
 	return 0;
c1bac6
 }
c1bac6
 
c1bac6
+/*
c1bac6
+ * stp_task_notify_resume() is our version of
c1bac6
+ * set_notify_resume(). When called, the task_work infrastructure will
c1bac6
+ * cause utrace_resume() to get called.
c1bac6
+ */
c1bac6
+static void
c1bac6
+stp_task_notify_resume(struct task_struct *target, struct utrace *utrace)
c1bac6
+{
c1bac6
+	if (! utrace->task_work_added) {
c1bac6
+		int rc = stp_task_work_add(target, &utrace->work);
c1bac6
+		if (rc == 0) {
c1bac6
+			utrace->task_work_added = 1;
c1bac6
+		}
c1bac6
+		/* stp_task_work_add() returns -ESRCH if the task has
c1bac6
+		 * already passed exit_task_work(). Just ignore this
c1bac6
+		 * error. */
c1bac6
+		else if (rc != -ESRCH) {
c1bac6
+			printk(KERN_ERR "%s:%d - task_work_add() returned %d\n",
c1bac6
+			       __FUNCTION__, __LINE__, rc);
c1bac6
+		}
c1bac6
+	}
c1bac6
+}
c1bac6
+
c1bac6
 static void utrace_resume(struct task_work *work);
c1bac6
 static void utrace_report_work(struct task_work *work);
c1bac6
 
c1bac6
@@ -348,21 +395,29 @@ static void utrace_cleanup(struct utrace *utrace)
c1bac6
 	}
c1bac6
 
c1bac6
 	if (utrace->task_work_added) {
c1bac6
+#ifdef STP_TF_DEBUG
c1bac6
 		if (stp_task_work_cancel(utrace->task, &utrace_resume) == NULL)
c1bac6
 			printk(KERN_ERR "%s:%d - task_work_cancel() failed? task %p, %d, %s\n",
c1bac6
 			       __FUNCTION__, __LINE__, utrace->task,
c1bac6
 			       utrace->task->tgid,
c1bac6
 			       (utrace->task->comm ? utrace->task->comm
c1bac6
 				: "UNKNOWN"));
c1bac6
+#else
c1bac6
+		stp_task_work_cancel(utrace->task, &utrace_resume);
c1bac6
+#endif
c1bac6
 		utrace->task_work_added = 0;
c1bac6
 	}
c1bac6
 	if (utrace->report_work_added) {
c1bac6
+#ifdef STP_TF_DEBUG
c1bac6
 		if (stp_task_work_cancel(utrace->task, &utrace_report_work) == NULL)
c1bac6
 			printk(KERN_ERR "%s:%d - task_work_cancel() failed? task %p, %d, %s\n",
c1bac6
 			       __FUNCTION__, __LINE__, utrace->task,
c1bac6
 			       utrace->task->tgid,
c1bac6
 			       (utrace->task->comm ? utrace->task->comm
c1bac6
 				: "UNKNOWN"));
c1bac6
+#else
c1bac6
+		stp_task_work_cancel(utrace->task, &utrace_report_work);
c1bac6
+#endif
c1bac6
 		utrace->report_work_added = 0;
c1bac6
 	}
c1bac6
 	spin_unlock(&utrace->lock);
c1bac6
@@ -945,7 +1000,11 @@ static int utrace_set_events(struct task_struct *target,
c1bac6
 	 * If utrace_report_death() is already progress now,
c1bac6
 	 * it's too late to clear the death event bits.
c1bac6
 	 */
c1bac6
-	if (((old_flags & ~events) & _UTRACE_DEATH_EVENTS) && utrace->death)
c1bac6
+	if (target->exit_state &&
c1bac6
+	    (((events & ~old_flags) & _UTRACE_DEATH_EVENTS) ||
c1bac6
+	     (utrace->death &&
c1bac6
+	      ((old_flags & ~events) & _UTRACE_DEATH_EVENTS)) ||
c1bac6
+	     (utrace->reap && ((old_flags & ~events) & UTRACE_EVENT(REAP)))))
c1bac6
 		goto unlock;
c1bac6
 
c1bac6
 	/*
c1bac6
@@ -1035,20 +1094,7 @@ static bool utrace_do_stop(struct task_struct *target, struct utrace *utrace)
c1bac6
 		spin_unlock_irq(&target->sighand->siglock);
c1bac6
 	} else if (utrace->resume > UTRACE_REPORT) {
c1bac6
 		utrace->resume = UTRACE_REPORT;
c1bac6
-		if (! utrace->task_work_added) {
c1bac6
-			int rc = stp_task_work_add(target, &utrace->work);
c1bac6
-			if (rc == 0) {
c1bac6
-				utrace->task_work_added = 1;
c1bac6
-			}
c1bac6
-			/* stp_task_work_add() returns -ESRCH if the task
c1bac6
-			 * has already passed exit_task_work(). Just
c1bac6
-			 * ignore this error. */
c1bac6
-			else if (rc != -ESRCH) {
c1bac6
-				printk(KERN_ERR
c1bac6
-				       "%s:%d - task_work_add() returned %d\n",
c1bac6
-				       __FUNCTION__, __LINE__, rc);
c1bac6
-			}
c1bac6
-		}
c1bac6
+		stp_task_notify_resume(target, utrace);
c1bac6
 	}
c1bac6
 
c1bac6
 	return task_is_traced(target);
c1bac6
@@ -1066,10 +1112,7 @@ static void utrace_wakeup(struct task_struct *target, struct utrace *utrace)
c1bac6
 	    target->signal->group_stop_count)
c1bac6
 		target->state = TASK_STOPPED;
c1bac6
 	else
c1bac6
-		/* FIXME: Had to change wake_up_state() to
c1bac6
-		 * wake_up_process() here (since wake_up_state() isn't
c1bac6
-		 * exported).  Reasonable? */
c1bac6
-		wake_up_process(target);
c1bac6
+		stp_wake_up_state(target, __TASK_TRACED);
c1bac6
 	spin_unlock_irq(&target->sighand->siglock);
c1bac6
 }
c1bac6
 
c1bac6
@@ -1175,20 +1218,9 @@ static void utrace_stop(struct task_struct *task, struct utrace *utrace,
c1bac6
 		 * Ensure a reporting pass when we're resumed.
c1bac6
 		 */
c1bac6
 		utrace->resume = action;
c1bac6
-		if (! utrace->task_work_added) {
c1bac6
-			int rc = stp_task_work_add(task, &utrace->work);
c1bac6
-			if (rc == 0) {
c1bac6
-				utrace->task_work_added = 1;
c1bac6
-			}
c1bac6
-			/* stp_task_work_add() returns -ESRCH if the task
c1bac6
-			 * has already passed exit_task_work(). Just
c1bac6
-			 * ignore this error. */
c1bac6
-			else if (rc != -ESRCH) {
c1bac6
-				printk(KERN_ERR
c1bac6
-				       "%s:%d - stp_task_work_add() returned %d\n",
c1bac6
-				       __FUNCTION__, __LINE__, rc);
c1bac6
-			}
c1bac6
-		}
c1bac6
+		stp_task_notify_resume(task, utrace);
c1bac6
+		if (action == UTRACE_INTERRUPT)
c1bac6
+			set_thread_flag(TIF_SIGPENDING);
c1bac6
 	}
c1bac6
 
c1bac6
 	/*
c1bac6
@@ -1540,21 +1572,7 @@ static int utrace_control(struct task_struct *target,
c1bac6
 		clear_engine_wants_stop(engine);
c1bac6
 		if (action < utrace->resume) {
c1bac6
 			utrace->resume = action;
c1bac6
-			if (! utrace->task_work_added) {
c1bac6
-				ret = stp_task_work_add(target, &utrace->work);
c1bac6
-				if (ret == 0) {
c1bac6
-					utrace->task_work_added = 1;
c1bac6
-				}
c1bac6
-				/* stp_task_work_add() returns -ESRCH if
c1bac6
-				 * the task has already passed
c1bac6
-				 * exit_task_work(). Just ignore this
c1bac6
-				 * error. */
c1bac6
-				else if (ret != -ESRCH) {
c1bac6
-					printk(KERN_ERR
c1bac6
-					       "%s:%d - stp_task_work_add() returned %d\n",
c1bac6
-					       __FUNCTION__, __LINE__, ret);
c1bac6
-				}
c1bac6
-			}
c1bac6
+			stp_task_notify_resume(target, utrace);
c1bac6
 		}
c1bac6
 		break;
c1bac6
 
c1bac6
@@ -1574,6 +1592,7 @@ static int utrace_control(struct task_struct *target,
c1bac6
 		 * we've serialized any later recalc_sigpending() after our
c1bac6
 		 * setting of utrace->resume to force it on.
c1bac6
 		 */
c1bac6
+		stp_task_notify_resume(target, utrace);
c1bac6
 		if (reset) {
c1bac6
 			/*
c1bac6
 			 * This is really just to keep the invariant that
c1bac6
@@ -1583,35 +1602,12 @@ static int utrace_control(struct task_struct *target,
c1bac6
 			 */
c1bac6
 			set_tsk_thread_flag(target, TIF_SIGPENDING);
c1bac6
 		} else {
c1bac6
-			int rc = 0;
c1bac6
-
c1bac6
-			if (! utrace->task_work_added) {
c1bac6
-				rc = stp_task_work_add(target, &utrace->work);
c1bac6
-				/* stp_task_work_add() returns -ESRCH
c1bac6
-				 * if the task has already passed
c1bac6
-				 * exit_task_work(). Just ignore this
c1bac6
-				 * error. */
c1bac6
-				if (rc == 0 || rc == -ESRCH) {
c1bac6
-					utrace->task_work_added = 1;
c1bac6
-					rc = 0;
c1bac6
-				}
c1bac6
-				else {
c1bac6
-					printk(KERN_ERR
c1bac6
-					       "%s:%d - task_work_add() returned %d\n",
c1bac6
-					       __FUNCTION__, __LINE__, rc);
c1bac6
-				}
c1bac6
-			}
c1bac6
-
c1bac6
-			if (likely(rc == 0)) {
c1bac6
-				struct sighand_struct *sighand;
c1bac6
-				unsigned long irqflags;
c1bac6
-
c1bac6
-				sighand = stp_lock_task_sighand(target,
c1bac6
-								&irqflags);
c1bac6
-				if (likely(sighand)) {
c1bac6
-					stp_signal_wake_up(target, 0);
c1bac6
-					unlock_task_sighand(target, &irqflags);
c1bac6
-				}
c1bac6
+			struct sighand_struct *sighand;
c1bac6
+			unsigned long irqflags;
c1bac6
+			sighand = stp_lock_task_sighand(target, &irqflags);
c1bac6
+			if (likely(sighand)) {
c1bac6
+				stp_signal_wake_up(target, 0);
c1bac6
+				unlock_task_sighand(target, &irqflags);
c1bac6
 			}
c1bac6
 		}
c1bac6
 		break;
c1bac6
@@ -1764,20 +1760,9 @@ static void finish_report(struct task_struct *task, struct utrace *utrace,
c1bac6
 	if (resume < utrace->resume) {
c1bac6
 		spin_lock(&utrace->lock);
c1bac6
 		utrace->resume = resume;
c1bac6
-		if (! utrace->task_work_added) {
c1bac6
-			int rc = stp_task_work_add(task, &utrace->work);
c1bac6
-			if (rc == 0) {
c1bac6
-				utrace->task_work_added = 1;
c1bac6
-			}
c1bac6
-			/* stp_task_work_add() returns -ESRCH if the task
c1bac6
-			 * has already passed exit_task_work(). Just
c1bac6
-			 * ignore this error. */
c1bac6
-			else if (rc != -ESRCH) {
c1bac6
-				printk(KERN_ERR
c1bac6
-				       "%s:%d - task_work_add() returned %d\n",
c1bac6
-				       __FUNCTION__, __LINE__, rc);
c1bac6
-			}
c1bac6
-		}
c1bac6
+		stp_task_notify_resume(task, utrace);
c1bac6
+		if (resume == UTRACE_INTERRUPT)
c1bac6
+			set_tsk_thread_flag(task, TIF_SIGPENDING);
c1bac6
 		spin_unlock(&utrace->lock);
c1bac6
 	}
c1bac6
 
c1bac6
@@ -2323,12 +2308,16 @@ static void finish_resume_report(struct task_struct *task,
c1bac6
 		utrace_stop(task, utrace, report->resume_action);
c1bac6
 		break;
c1bac6
 
c1bac6
+	case UTRACE_INTERRUPT:
c1bac6
+		if (!signal_pending(task)) {
c1bac6
+			stp_task_notify_resume(task, utrace);
c1bac6
+			set_tsk_thread_flag(task, TIF_SIGPENDING);
c1bac6
+		}
c1bac6
+		break;
c1bac6
+
c1bac6
 	case UTRACE_REPORT:
c1bac6
 	case UTRACE_RESUME:
c1bac6
 	default:
c1bac6
-#if 0
c1bac6
-		user_disable_single_step(task);
c1bac6
-#endif
c1bac6
 		break;
c1bac6
 	}
c1bac6
 }
c1bac6
@@ -2385,6 +2374,14 @@ static void utrace_resume(struct task_work *work)
c1bac6
 		/* Remember that this task_work_func is finished. */
c1bac6
 		stp_task_work_func_done();
c1bac6
 		return;
c1bac6
+	case UTRACE_INTERRUPT:
c1bac6
+		/*
c1bac6
+		 * Note that UTRACE_INTERRUPT reporting was handled by
c1bac6
+		 * utrace_get_signal() in original utrace. In this
c1bac6
+		 * utrace version, we'll handle it here like UTRACE_REPORT.
c1bac6
+		 *
c1bac6
+		 * Fallthrough...
c1bac6
+		 */
c1bac6
 	case UTRACE_REPORT:
c1bac6
 		if (unlikely(!(utrace->utrace_flags & UTRACE_EVENT(QUIESCE))))
c1bac6
 			break;
c1bac6
diff --git a/testsuite/systemtap.base/process_resume.c b/testsuite/systemtap.base/process_resume.c
c1bac6
new file mode 100644
c1bac6
index 000000000000..ddf8b1eaf795
c1bac6
--- /dev/null
c1bac6
+++ b/testsuite/systemtap.base/process_resume.c
c1bac6
@@ -0,0 +1,10 @@
c1bac6
+#include <sys/types.h>
c1bac6
+#include <unistd.h>
c1bac6
+#include <signal.h>
c1bac6
+ 
c1bac6
+int main()
c1bac6
+{
c1bac6
+    kill(getpid(), SIGSTOP);
c1bac6
+    getpid();
c1bac6
+    return 0;
c1bac6
+}
c1bac6
diff --git a/testsuite/systemtap.base/process_resume.exp b/testsuite/systemtap.base/process_resume.exp
c1bac6
new file mode 100644
c1bac6
index 000000000000..0cb4a4322efe
c1bac6
--- /dev/null
c1bac6
+++ b/testsuite/systemtap.base/process_resume.exp
c1bac6
@@ -0,0 +1,82 @@
c1bac6
+# This test answers the following questions:
c1bac6
+#
c1bac6
+# - Can we attach to stopped processes correctly? Before PR?????? fix,
c1bac6
+#   stap would cause a kernel warn.
c1bac6
+#
c1bac6
+# - Does resuming work on those processes? Before PR?????? fix,
c1bac6
+#   processes would get stuck.
c1bac6
+
c1bac6
+set test "process_resume"
c1bac6
+
c1bac6
+# Only run on make installcheck and if we have utrace
c1bac6
+if {![installtest_p]} { untested "$test"; return }
c1bac6
+if {![utrace_p]} { untested "$test : no kernel utrace support found"; return }
c1bac6
+
c1bac6
+# "load" generation function for stap_run.  It waits a bit, tells the
c1bac6
+# test program to continue and then waits a bit.
c1bac6
+proc resume_load {} {
c1bac6
+    global pid
c1bac6
+
c1bac6
+    wait_n_secs 2
c1bac6
+    kill -cont $pid 0
c1bac6
+    wait_n_secs 2
c1bac6
+    return 0
c1bac6
+}
c1bac6
+
c1bac6
+set getpid_script {
c1bac6
+    global getpid_calls = 0
c1bac6
+    probe begin { printf("systemtap starting probe\n") }
c1bac6
+    probe syscall.getpid { getpid_calls++ }
c1bac6
+    probe end { printf("systemtap ending probe\n")
c1bac6
+	printf("getpid calls = %d\n", getpid_calls) }
c1bac6
+}
c1bac6
+# Notice we don't care how many getpid() calls we see.
c1bac6
+set getpid_script_output "getpid calls = \[0-9\]\r\n"
c1bac6
+
c1bac6
+set end_script {
c1bac6
+    global end_probes_fired = 0
c1bac6
+    probe begin { printf("systemtap starting probe\n") }
c1bac6
+    probe process("%s").end { end_probes_fired++ }
c1bac6
+    probe end { printf("systemtap ending probe\n")
c1bac6
+	printf("end probes = %%d\n", end_probes_fired) }
c1bac6
+}
c1bac6
+set end_script_output "end probes = 1\r\n"
c1bac6
+
c1bac6
+# Compile test program.
c1bac6
+set res [target_compile $srcdir/$subdir/${test}.c ${test} executable \
c1bac6
+    "additional_flags=-O2 additional_flags=-g"]
c1bac6
+if { $res != "" } {
c1bac6
+    verbose "target_compile failed: $res" 2
c1bac6
+    fail "${test}: unable to compile ${test}.c"
c1bac6
+    return
c1bac6
+}
c1bac6
+
c1bac6
+# Start the application and put it in the background before each
c1bac6
+# test. It will stop itself and be resumed by the 'resume_load' proc.
c1bac6
+
c1bac6
+set TEST_NAME "${test}-getpid"
c1bac6
+set pid [exec ./$test &]
c1bac6
+#spawn "./$test"
c1bac6
+#set id $spawn_id
c1bac6
+#set pid [exp_pid -i $spawn_id]
c1bac6
+stap_run $TEST_NAME resume_load $getpid_script_output -e $getpid_script
c1bac6
+if {[file isdirectory /proc/$pid]} {
c1bac6
+    kill -KILL $pid 0
c1bac6
+    fail "$TEST_NAME: process didn't resume properly"
c1bac6
+} else {
c1bac6
+    pass "$TEST_NAME: process resumed properly"
c1bac6
+}
c1bac6
+
c1bac6
+set TEST_NAME "${test}-end"
c1bac6
+set pid [exec ./$test &]
c1bac6
+set script [format $end_script $test]
c1bac6
+stap_run $TEST_NAME resume_load $end_script_output -e $script
c1bac6
+if {[file isdirectory /proc/$pid]} {
c1bac6
+    kill -KILL $pid 0
c1bac6
+    fail "$TEST_NAME: process didn't resume properly"
c1bac6
+} else {
c1bac6
+    pass "$TEST_NAME: process resumed properly"
c1bac6
+}
c1bac6
+
c1bac6
+# Cleanup
c1bac6
+if { $verbose == 0 } { catch { exec rm -f $test } }