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

Commit e1f7a63a authored by Christopher Ferris's avatar Christopher Ferris
Browse files

Add a few leak check tests.

These tests are meant to replace the leak check tests from libbacktrace.

Bug: 120606663

Test: Ran tests on host and target.
Change-Id: I928b199304afc36b4bac78e9a2cd688b6f2910b9
parent c87d1ed6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -212,6 +212,7 @@ cc_test {
        "tests/RegsStepIfSignalHandlerTest.cpp",
        "tests/RegsTest.cpp",
        "tests/SymbolsTest.cpp",
        "tests/TestUtils.cpp",
        "tests/UnwindOfflineTest.cpp",
        "tests/UnwindTest.cpp",
        "tests/UnwinderTest.cpp",
+44 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.
 */

#include <malloc.h>
#include <stdint.h>

#include <gtest/gtest.h>

namespace unwindstack {

void TestCheckForLeaks(void (*unwind_func)(void*), void* data) {
  static constexpr size_t kNumLeakLoops = 200;
  static constexpr size_t kMaxAllowedLeakBytes = 32 * 1024;

  size_t first_allocated_bytes = 0;
  size_t last_allocated_bytes = 0;
  for (size_t i = 0; i < kNumLeakLoops; i++) {
    unwind_func(data);

    size_t allocated_bytes = mallinfo().uordblks;
    if (first_allocated_bytes == 0) {
      first_allocated_bytes = allocated_bytes;
    } else if (last_allocated_bytes > first_allocated_bytes) {
      // Check that the memory did not increase too much over the first loop.
      ASSERT_LE(last_allocated_bytes - first_allocated_bytes, kMaxAllowedLeakBytes);
    }
    last_allocated_bytes = allocated_bytes;
  }
}

}  // namespace unwindstack
+2 −0
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@ inline bool TestQuiescePid(pid_t pid) {
  return ready;
}

void TestCheckForLeaks(void (*unwind_func)(void*), void* data);

}  // namespace unwindstack

#endif  // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+38 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@
#include <unwindstack/Unwinder.h>

#include "ElfTestUtils.h"
#include "TestUtils.h"

namespace unwindstack {

@@ -901,6 +902,43 @@ TEST_F(UnwindOfflineTest, jit_debug_arm) {
  EXPECT_EQ(0xff85f0c0U, unwinder.frames()[75].sp);
}

struct LeakType {
  LeakType(Maps* maps, Regs* regs, std::shared_ptr<Memory>& process_memory)
      : maps(maps), regs(regs), process_memory(process_memory) {}

  Maps* maps;
  Regs* regs;
  std::shared_ptr<Memory>& process_memory;
};

static void OfflineUnwind(void* data) {
  LeakType* leak_data = reinterpret_cast<LeakType*>(data);

  std::unique_ptr<Regs> regs_copy(leak_data->regs->Clone());
  JitDebug jit_debug(leak_data->process_memory);
  Unwinder unwinder(128, leak_data->maps, regs_copy.get(), leak_data->process_memory);
  unwinder.SetJitDebug(&jit_debug, regs_copy->Arch());
  unwinder.Unwind();
  ASSERT_EQ(76U, unwinder.NumFrames());
}

TEST_F(UnwindOfflineTest, unwind_offline_check_for_leaks) {
  ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));

  MemoryOfflineParts* memory = new MemoryOfflineParts;
  AddMemory(dir_ + "descriptor.data", memory);
  AddMemory(dir_ + "descriptor1.data", memory);
  AddMemory(dir_ + "stack.data", memory);
  for (size_t i = 0; i < 7; i++) {
    AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
    AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
  }
  process_memory_.reset(memory);

  LeakType data(maps_.get(), regs_.get(), process_memory_);
  TestCheckForLeaks(OfflineUnwind, &data);
}

// The eh_frame_hdr data is present but set to zero fdes. This should
// fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
// No .gnu_debugdata section in the elf file, so no symbols.
+66 −0
Original line number Diff line number Diff line
@@ -198,6 +198,21 @@ TEST_F(UnwindTest, local_use_from_pid) {
  OuterFunction(TEST_TYPE_LOCAL_UNWINDER_FROM_PID);
}

static void LocalUnwind(void* data) {
  TestTypeEnum* test_type = reinterpret_cast<TestTypeEnum*>(data);
  OuterFunction(*test_type);
}

TEST_F(UnwindTest, local_check_for_leak) {
  TestTypeEnum test_type = TEST_TYPE_LOCAL_UNWINDER;
  TestCheckForLeaks(LocalUnwind, &test_type);
}

TEST_F(UnwindTest, local_use_from_pid_check_for_leak) {
  TestTypeEnum test_type = TEST_TYPE_LOCAL_UNWINDER_FROM_PID;
  TestCheckForLeaks(LocalUnwind, &test_type);
}

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
@@ -279,6 +294,57 @@ TEST_F(UnwindTest, unwind_from_pid_remote) {
      << "ptrace detach failed with unexpected error: " << strerror(errno);
}

static void RemoteCheckForLeaks(void (*unwind_func)(void*)) {
  pid_t pid;
  if ((pid = fork()) == 0) {
    OuterFunction(TEST_TYPE_REMOTE);
    exit(0);
  }
  ASSERT_NE(-1, pid);
  TestScopedPidReaper reap(pid);

  bool completed;
  WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
  ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";

  TestCheckForLeaks(unwind_func, &pid);

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

static void RemoteUnwind(void* data) {
  pid_t* pid = reinterpret_cast<pid_t*>(data);

  RemoteMaps maps(*pid);
  ASSERT_TRUE(maps.Parse());
  std::unique_ptr<Regs> regs(Regs::RemoteGet(*pid));
  ASSERT_TRUE(regs.get() != nullptr);

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

TEST_F(UnwindTest, remote_check_for_leaks) {
  RemoteCheckForLeaks(RemoteUnwind);
}

static void RemoteUnwindFromPid(void* data) {
  pid_t* pid = reinterpret_cast<pid_t*>(data);

  std::unique_ptr<Regs> regs(Regs::RemoteGet(*pid));
  ASSERT_TRUE(regs.get() != nullptr);

  UnwinderFromPid unwinder(512, *pid);
  ASSERT_TRUE(unwinder.Init(regs->Arch()));
  unwinder.SetRegs(regs.get());

  VerifyUnwind(&unwinder, kFunctionOrder);
}

TEST_F(UnwindTest, remote_unwind_for_pid_check_for_leaks) {
  RemoteCheckForLeaks(RemoteUnwindFromPid);
}

TEST_F(UnwindTest, from_context) {
  std::atomic_int tid(0);
  std::thread thread([&]() {