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
This commit is contained in:
Christopher Ferris 2019-01-24 12:22:03 -08:00
parent c87d1ed611
commit e1f7a63a4d
5 changed files with 151 additions and 0 deletions

View file

@ -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",

View file

@ -0,0 +1,44 @@
/*
* 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

View file

@ -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

View file

@ -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.

View file

@ -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([&]() {