Merge "Add a few leak check tests." am: ed7ca8bb14
am: 9a96618c9d
Change-Id: I2966cccd7712c04c95be5090b1e96d8600b3d5d4
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index d1b8271..c90f5b2 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -212,6 +212,7 @@
"tests/RegsStepIfSignalHandlerTest.cpp",
"tests/RegsTest.cpp",
"tests/SymbolsTest.cpp",
+ "tests/TestUtils.cpp",
"tests/UnwindOfflineTest.cpp",
"tests/UnwindTest.cpp",
"tests/UnwinderTest.cpp",
diff --git a/libunwindstack/tests/TestUtils.cpp b/libunwindstack/tests/TestUtils.cpp
new file mode 100644
index 0000000..e76f5f8
--- /dev/null
+++ b/libunwindstack/tests/TestUtils.cpp
@@ -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
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
index 8c31aa6..a4d7b9b 100644
--- a/libunwindstack/tests/TestUtils.h
+++ b/libunwindstack/tests/TestUtils.h
@@ -50,6 +50,8 @@
return ready;
}
+void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
+
} // namespace unwindstack
#endif // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 0588a84..b5feb38 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -43,6 +43,7 @@
#include <unwindstack/Unwinder.h>
#include "ElfTestUtils.h"
+#include "TestUtils.h"
namespace unwindstack {
@@ -901,6 +902,43 @@
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.
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index c747eab..4e38015 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -198,6 +198,21 @@
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 @@
<< "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([&]() {