Merge "Add a few leak check tests." am: ed7ca8bb14
am: 9a96618c9d
Change-Id: I2966cccd7712c04c95be5090b1e96d8600b3d5d4
This commit is contained in:
commit
1efb1601bf
5 changed files with 151 additions and 0 deletions
|
|
@ -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
libunwindstack/tests/TestUtils.cpp
Normal file
44
libunwindstack/tests/TestUtils.cpp
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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([&]() {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue