Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cfadedb1 authored by Christopher Ferris's avatar Christopher Ferris Committed by Gerrit Code Review
Browse files

Merge "Fix UnwindTest repeatability."

parents 16dd491a edccd847
Loading
Loading
Loading
Loading
+5 −26
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include <vector>
@@ -34,32 +33,18 @@
#include <unwindstack/Memory.h>

#include "MemoryFake.h"
#include "TestUtils.h"

namespace unwindstack {

class MemoryRemoteTest : public ::testing::Test {
 protected:
  static uint64_t NanoTime() {
    struct timespec t = { 0, 0 };
    clock_gettime(CLOCK_MONOTONIC, &t);
    return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
  }

  static bool Attach(pid_t pid) {
    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
      return false;
    }

    uint64_t start = NanoTime();
    siginfo_t si;
    while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
      if ((NanoTime() - start) > 10 * NS_PER_SEC) {
        printf("%d: Failed to stop after 10 seconds.\n", pid);
        return false;
      }
      usleep(30);
    }
    return true;
    return TestQuiescePid(pid);
  }

  static bool Detach(pid_t pid) {
@@ -79,6 +64,7 @@ TEST_F(MemoryRemoteTest, read) {
    exit(1);
  }
  ASSERT_LT(0, pid);
  TestScopedPidReaper reap(pid);

  ASSERT_TRUE(Attach(pid));

@@ -91,9 +77,6 @@ TEST_F(MemoryRemoteTest, read) {
  }

  ASSERT_TRUE(Detach(pid));

  kill(pid, SIGKILL);
  ASSERT_EQ(pid, wait(nullptr));
}

TEST_F(MemoryRemoteTest, read_fail) {
@@ -111,6 +94,7 @@ TEST_F(MemoryRemoteTest, read_fail) {
    exit(1);
  }
  ASSERT_LT(0, pid);
  TestScopedPidReaper reap(pid);

  ASSERT_TRUE(Attach(pid));

@@ -132,9 +116,6 @@ TEST_F(MemoryRemoteTest, read_fail) {
  ASSERT_EQ(0, munmap(src, pagesize));

  ASSERT_TRUE(Detach(pid));

  kill(pid, SIGKILL);
  ASSERT_EQ(pid, wait(nullptr));
}

TEST_F(MemoryRemoteTest, read_overflow) {
@@ -152,6 +133,7 @@ TEST_F(MemoryRemoteTest, read_illegal) {
    exit(1);
  }
  ASSERT_LT(0, pid);
  TestScopedPidReaper reap(pid);

  ASSERT_TRUE(Attach(pid));

@@ -162,9 +144,6 @@ TEST_F(MemoryRemoteTest, read_illegal) {
  ASSERT_FALSE(remote.Read(0, dst.data(), 100));

  ASSERT_TRUE(Detach(pid));

  kill(pid, SIGKILL);
  ASSERT_EQ(pid, wait(nullptr));
}

}  // namespace unwindstack
+55 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
#define _LIBUNWINDSTACK_TESTS_TEST_UTILS_H

#include <signal.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>

namespace unwindstack {

class TestScopedPidReaper {
 public:
  TestScopedPidReaper(pid_t pid) : pid_(pid) {}
  ~TestScopedPidReaper() {
    kill(pid_, SIGKILL);
    waitpid(pid_, nullptr, 0);
  }

 private:
  pid_t pid_;
};

inline bool TestQuiescePid(pid_t pid) {
  siginfo_t si;
  bool ready = false;
  // Wait for up to 5 seconds.
  for (size_t i = 0; i < 5000; i++) {
    if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
      ready = true;
      break;
    }
    usleep(1000);
  }
  return ready;
}

}  // namespace unwindstack

#endif  // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+51 −45
Original line number Diff line number Diff line
@@ -15,10 +15,9 @@
 */

#include <errno.h>
#include <string.h>

#include <signal.h>
#include <stdint.h>
#include <string.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <unistd.h>
@@ -39,14 +38,24 @@
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>

#include "TestUtils.h"

namespace unwindstack {

static std::atomic_bool g_ready(false);
static volatile bool g_ready_for_remote = false;
static volatile bool g_signal_ready_for_remote = false;
static std::atomic_bool g_finish(false);
static std::atomic_bool g_ready;
static volatile bool g_ready_for_remote;
static volatile bool g_signal_ready_for_remote;
static std::atomic_bool g_finish;
static std::atomic_uintptr_t g_ucontext;

static void ResetGlobals() {
  g_ready = false;
  g_ready_for_remote = false;
  g_signal_ready_for_remote = false;
  g_finish = false;
  g_ucontext = 0;
}

static std::vector<const char*> kFunctionOrder{"InnerFunction", "MiddleFunction", "OuterFunction"};

static std::vector<const char*> kFunctionSignalOrder{"SignalInnerFunction", "SignalMiddleFunction",
@@ -156,48 +165,52 @@ extern "C" void OuterFunction(bool local) {
  MiddleFunction(local);
}

TEST(UnwindTest, local) {
class UnwindTest : public ::testing::Test {
 public:
  void SetUp() override { ResetGlobals(); }
};

TEST_F(UnwindTest, local) {
  OuterFunction(true);
}

void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
  *completed = false;
  // Need to sleep before attempting first ptrace. Without this, on the
  // host it becomes impossible to attach and ptrace set errno to EPERM.
  // host it becomes impossible to attach and ptrace sets errno to EPERM.
  usleep(1000);
  for (size_t i = 0; i < 100; i++) {
    ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0));
    for (size_t j = 0; j < 100; j++) {
      siginfo_t si;
      if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
  for (size_t i = 0; i < 1000; i++) {
    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
      ASSERT_TRUE(TestQuiescePid(pid))
          << "Waiting for process to quiesce failed: " << strerror(errno);

      MemoryRemote memory(pid);
      // Read the remote value to see if we are ready.
      bool value;
      if (memory.Read(addr, &value, sizeof(value)) && value) {
        *completed = true;
          break;
        }
      }
      usleep(1000);
    }
    if (leave_attached && *completed) {
      break;
      }
      if (!*completed || !leave_attached) {
        ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
      }
      if (*completed) {
        break;
      }
    usleep(1000);
    } else {
      ASSERT_EQ(ESRCH, errno) << "ptrace attach failed with unexpected error: " << strerror(errno);
    }
    usleep(5000);
  }
}

TEST(UnwindTest, remote) {
TEST_F(UnwindTest, remote) {
  pid_t pid;
  if ((pid = fork()) == 0) {
    OuterFunction(false);
    exit(0);
  }
  ASSERT_NE(-1, pid);
  TestScopedPidReaper reap(pid);

  bool completed;
  WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
@@ -210,13 +223,11 @@ TEST(UnwindTest, remote) {

  VerifyUnwind(pid, &maps, regs.get(), kFunctionOrder);

  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));

  kill(pid, SIGKILL);
  ASSERT_EQ(pid, wait(nullptr));
  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
      << "ptrace detach failed with unexpected error: " << strerror(errno);
}

TEST(UnwindTest, from_context) {
TEST_F(UnwindTest, from_context) {
  std::atomic_int tid(0);
  std::thread thread([&]() {
    tid = syscall(__NR_gettid);
@@ -263,10 +274,6 @@ TEST(UnwindTest, from_context) {
}

static void RemoteThroughSignal(unsigned int sa_flags) {
  g_ready = false;
  g_signal_ready_for_remote = false;
  g_finish = false;

  pid_t pid;
  if ((pid = fork()) == 0) {
    struct sigaction act, oldact;
@@ -279,6 +286,7 @@ static void RemoteThroughSignal(unsigned int sa_flags) {
    exit(0);
  }
  ASSERT_NE(-1, pid);
  TestScopedPidReaper reap(pid);

  bool completed;
  WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
@@ -294,17 +302,15 @@ static void RemoteThroughSignal(unsigned int sa_flags) {

  VerifyUnwind(pid, &maps, regs.get(), kFunctionSignalOrder);

  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));

  kill(pid, SIGKILL);
  ASSERT_EQ(pid, wait(nullptr));
  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
      << "ptrace detach failed with unexpected error: " << strerror(errno);
}

TEST(UnwindTest, remote_through_signal) {
TEST_F(UnwindTest, remote_through_signal) {
  RemoteThroughSignal(0);
}

TEST(UnwindTest, remote_through_signal_sa_siginfo) {
TEST_F(UnwindTest, remote_through_signal_sa_siginfo) {
  RemoteThroughSignal(SA_SIGINFO);
}