Add more ptrace process resumption tests.

Add tests to verify that ptrace unlink happens immediately for unreaped
processes.

Test: /data/nativetest/bionic-unit-tests/bionic-unit-tests --gtest_filter="Ptrace*"
Test: /data/nativetest64/bionic-unit-tests/bionic-unit-tests --gtest_filter="Ptrace*"
Change-Id: I9803ee5be2a0686c21556598ecf17348df09f601
diff --git a/tests/sys_ptrace_test.cpp b/tests/sys_ptrace_test.cpp
index bce5898..69638be 100644
--- a/tests/sys_ptrace_test.cpp
+++ b/tests/sys_ptrace_test.cpp
@@ -17,6 +17,7 @@
 #include <sys/ptrace.h>
 
 #include <elf.h>
+#include <err.h>
 #include <fcntl.h>
 #include <sched.h>
 #include <sys/prctl.h>
@@ -26,11 +27,16 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <chrono>
+#include <thread>
+
 #include <gtest/gtest.h>
 
 #include <android-base/macros.h>
 #include <android-base/unique_fd.h>
 
+using namespace std::chrono_literals;
+
 using android::base::unique_fd;
 
 // Host libc does not define this.
@@ -367,31 +373,39 @@
 
 class PtraceResumptionTest : public ::testing::Test {
  public:
+  unique_fd worker_pipe_write;
+
   pid_t worker = -1;
+  pid_t tracer = -1;
+
   PtraceResumptionTest() {
+    unique_fd worker_pipe_read;
+    int pipefd[2];
+    if (pipe2(pipefd, O_CLOEXEC) != 0) {
+      err(1, "failed to create pipe");
+    }
+
+    worker_pipe_read.reset(pipefd[0]);
+    worker_pipe_write.reset(pipefd[1]);
+
+    worker = fork();
+    if (worker == -1) {
+      err(1, "failed to fork worker");
+    } else if (worker == 0) {
+      char buf;
+      worker_pipe_write.reset();
+      TEMP_FAILURE_RETRY(read(worker_pipe_read.get(), &buf, sizeof(buf)));
+      exit(0);
+    }
   }
 
   ~PtraceResumptionTest() {
   }
 
   void AssertDeath(int signo);
-  void Start(std::function<void()> f) {
-    unique_fd worker_pipe_read, worker_pipe_write;
-    int pipefd[2];
-    ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
-    worker_pipe_read.reset(pipefd[0]);
-    worker_pipe_write.reset(pipefd[1]);
 
-    worker = fork();
-    ASSERT_NE(-1, worker);
-    if (worker == 0) {
-      char buf;
-      worker_pipe_write.reset();
-      TEMP_FAILURE_RETRY(read(worker_pipe_read.get(), &buf, sizeof(buf)));
-      exit(0);
-    }
-
-    pid_t tracer = fork();
+  void StartTracer(std::function<void()> f) {
+    tracer = fork();
     ASSERT_NE(-1, tracer);
     if (tracer == 0) {
       f();
@@ -400,26 +414,66 @@
       }
       exit(0);
     }
+  }
+
+  bool WaitForTracer() {
+    if (tracer == -1) {
+      errx(1, "tracer not started");
+    }
 
     int result;
     pid_t rc = waitpid(tracer, &result, 0);
-    ASSERT_EQ(tracer, rc);
-    EXPECT_TRUE(WIFEXITED(result) || WIFSIGNALED(result));
+    if (rc != tracer) {
+      printf("waitpid returned %d (%s)\n", rc, strerror(errno));
+      return false;
+    }
+
+    if (!WIFEXITED(result) && !WIFSIGNALED(result)) {
+      printf("!WIFEXITED && !WIFSIGNALED\n");
+      return false;
+    }
+
     if (WIFEXITED(result)) {
       if (WEXITSTATUS(result) != 0) {
-        FAIL() << "tracer failed";
+        printf("tracer failed\n");
+        return false;
       }
     }
 
-    rc = waitpid(worker, &result, WNOHANG);
-    ASSERT_EQ(0, rc);
+    return true;
+  }
+
+  bool WaitForWorker() {
+    if (worker == -1) {
+      errx(1, "worker not started");
+    }
+
+    int result;
+    pid_t rc = waitpid(worker, &result, WNOHANG);
+    if (rc != 0) {
+      printf("worker exited prematurely\n");
+      return false;
+    }
 
     worker_pipe_write.reset();
 
     rc = waitpid(worker, &result, 0);
-    ASSERT_EQ(worker, rc);
-    EXPECT_TRUE(WIFEXITED(result));
-    EXPECT_EQ(WEXITSTATUS(result), 0);
+    if (rc != worker) {
+      printf("waitpid for worker returned %d (%s)\n", rc, strerror(errno));
+      return false;
+    }
+
+    if (!WIFEXITED(result)) {
+      printf("worker didn't exit\n");
+      return false;
+    }
+
+    if (WEXITSTATUS(result) != 0) {
+      printf("worker exited with status %d\n", WEXITSTATUS(result));
+      return false;
+    }
+
+    return true;
   }
 };
 
@@ -436,22 +490,74 @@
   }
 }
 
+TEST_F(PtraceResumptionTest, smoke) {
+  // Make sure that the worker doesn't exit before the tracer stops tracing.
+  StartTracer([this]() {
+    ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
+    ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
+    wait_for_ptrace_stop(worker);
+    std::this_thread::sleep_for(500ms);
+  });
+
+  worker_pipe_write.reset();
+  std::this_thread::sleep_for(250ms);
+
+  int result;
+  ASSERT_EQ(0, waitpid(worker, &result, WNOHANG));
+  ASSERT_TRUE(WaitForTracer());
+  ASSERT_EQ(worker, waitpid(worker, &result, 0));
+}
+
 TEST_F(PtraceResumptionTest, seize) {
-  Start([this]() { ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno); });
+  StartTracer([this]() { ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno); });
+  ASSERT_TRUE(WaitForTracer());
+  ASSERT_TRUE(WaitForWorker());
 }
 
 TEST_F(PtraceResumptionTest, seize_interrupt) {
-  Start([this]() {
+  StartTracer([this]() {
     ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
     ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
+    wait_for_ptrace_stop(worker);
   });
+  ASSERT_TRUE(WaitForTracer());
+  ASSERT_TRUE(WaitForWorker());
 }
 
 TEST_F(PtraceResumptionTest, seize_interrupt_cont) {
-  Start([this]() {
+  StartTracer([this]() {
     ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
     ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
     wait_for_ptrace_stop(worker);
     ASSERT_EQ(0, ptrace(PTRACE_CONT, worker, 0, 0)) << strerror(errno);
   });
+  ASSERT_TRUE(WaitForTracer());
+  ASSERT_TRUE(WaitForWorker());
+}
+
+TEST_F(PtraceResumptionTest, zombie_seize) {
+  StartTracer([this]() { ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno); });
+  ASSERT_TRUE(WaitForWorker());
+  ASSERT_TRUE(WaitForTracer());
+}
+
+TEST_F(PtraceResumptionTest, zombie_seize_interrupt) {
+  StartTracer([this]() {
+    ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
+    ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
+    wait_for_ptrace_stop(worker);
+  });
+  ASSERT_TRUE(WaitForWorker());
+  ASSERT_TRUE(WaitForTracer());
+}
+
+TEST_F(PtraceResumptionTest, zombie_seize_interrupt_cont) {
+  StartTracer([this]() {
+    ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
+    ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
+    wait_for_ptrace_stop(worker);
+    ASSERT_EQ(0, ptrace(PTRACE_CONT, worker, 0, 0)) << strerror(errno);
+  });
+  ASSERT_TRUE(WaitForWorker());
+  ASSERT_TRUE(WaitForTracer());
 }