summaryrefslogtreecommitdiffstats
path: root/libbacktrace/backtrace_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libbacktrace/backtrace_test.cpp')
-rw-r--r--libbacktrace/backtrace_test.cpp203
1 files changed, 195 insertions, 8 deletions
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 23eaf92..ed6b211 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -16,9 +16,10 @@
#include <dirent.h>
#include <errno.h>
+#include <inttypes.h>
#include <pthread.h>
#include <signal.h>
-#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -32,9 +33,13 @@
#include <backtrace/BacktraceMap.h>
#include <UniquePtr.h>
+// For the THREAD_SIGNAL definition.
+#include "BacktraceThread.h"
+
#include <cutils/atomic.h>
#include <gtest/gtest.h>
+#include <algorithm>
#include <vector>
#include "thread_utils.h"
@@ -287,7 +292,7 @@ TEST(libbacktrace, ptrace_trace) {
pid_t pid;
if ((pid = fork()) == 0) {
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
- exit(1);
+ _exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump);
@@ -300,7 +305,7 @@ TEST(libbacktrace, ptrace_trace_shared_map) {
pid_t pid;
if ((pid = fork()) == 0) {
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
- exit(1);
+ _exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump);
@@ -314,7 +319,7 @@ TEST(libbacktrace, ptrace_max_trace) {
pid_t pid;
if ((pid = fork()) == 0) {
ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0);
- exit(1);
+ _exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump);
@@ -339,7 +344,7 @@ TEST(libbacktrace, ptrace_ignore_frames) {
pid_t pid;
if ((pid = fork()) == 0) {
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
- exit(1);
+ _exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
@@ -384,7 +389,7 @@ TEST(libbacktrace, ptrace_threads) {
ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0);
}
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
- exit(1);
+ _exit(1);
}
// Check to see that all of the threads are running before unwinding.
@@ -458,9 +463,15 @@ TEST(libbacktrace, thread_level_trace) {
// Wait up to 2 seconds for the tid to be set.
ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+ // Make sure that the thread signal used is not visible when compiled for
+ // the target.
+#if !defined(__GLIBC__)
+ ASSERT_LT(THREAD_SIGNAL, SIGRTMIN);
+#endif
+
// Save the current signal action and make sure it is restored afterwards.
struct sigaction cur_action;
- ASSERT_TRUE(sigaction(SIGURG, NULL, &cur_action) == 0);
+ ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &cur_action) == 0);
UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(backtrace.get() != NULL);
@@ -473,7 +484,7 @@ TEST(libbacktrace, thread_level_trace) {
// Verify that the old action was restored.
struct sigaction new_action;
- ASSERT_TRUE(sigaction(SIGURG, NULL, &new_action) == 0);
+ ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &new_action) == 0);
EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction);
EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
}
@@ -604,6 +615,49 @@ TEST(libbacktrace, thread_multiple_dump) {
}
}
+TEST(libbacktrace, thread_multiple_dump_same_thread) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ thread_t runner;
+ runner.tid = 0;
+ runner.state = 0;
+ ASSERT_TRUE(pthread_create(&runner.threadId, &attr, ThreadMaxRun, &runner) == 0);
+
+ // Wait for tids to be set.
+ ASSERT_TRUE(WaitForNonZero(&runner.state, 10));
+
+ // Start all of the dumpers at once, they will spin until they are signalled
+ // to begin their dump run.
+ int32_t dump_now = 0;
+ // Dump the same thread NUM_THREADS simultaneously.
+ std::vector<dump_thread_t> dumpers(NUM_THREADS);
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ dumpers[i].thread.tid = runner.tid;
+ dumpers[i].thread.state = 0;
+ dumpers[i].done = 0;
+ dumpers[i].now = &dump_now;
+
+ ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
+ }
+
+ // Start all of the dumpers going at once.
+ android_atomic_acquire_store(1, &dump_now);
+
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 100));
+
+ ASSERT_TRUE(dumpers[i].backtrace != NULL);
+ VerifyMaxDump(dumpers[i].backtrace);
+
+ delete dumpers[i].backtrace;
+ dumpers[i].backtrace = NULL;
+ }
+
+ // Tell the runner thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &runner.state);
+}
+
// This test is for UnwindMaps that should share the same map cursor when
// multiple maps are created for the current process at the same time.
TEST(libbacktrace, simultaneous_maps) {
@@ -693,3 +747,136 @@ TEST(libbacktrace, format_test) {
#endif
backtrace->FormatFrameData(&frame));
}
+
+struct map_test_t {
+ uintptr_t start;
+ uintptr_t end;
+};
+
+bool map_sort(map_test_t i, map_test_t j) {
+ return i.start < j.start;
+}
+
+static void VerifyMap(pid_t pid) {
+ char buffer[4096];
+ snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid);
+
+ FILE* map_file = fopen(buffer, "r");
+ ASSERT_TRUE(map_file != NULL);
+ std::vector<map_test_t> test_maps;
+ while (fgets(buffer, sizeof(buffer), map_file)) {
+ map_test_t map;
+ ASSERT_EQ(2, sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR " ", &map.start, &map.end));
+ test_maps.push_back(map);
+ }
+ fclose(map_file);
+ std::sort(test_maps.begin(), test_maps.end(), map_sort);
+
+ UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid));
+
+ // Basic test that verifies that the map is in the expected order.
+ std::vector<map_test_t>::const_iterator test_it = test_maps.begin();
+ for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
+ ASSERT_TRUE(test_it != test_maps.end());
+ ASSERT_EQ(test_it->start, it->start);
+ ASSERT_EQ(test_it->end, it->end);
+ ++test_it;
+ }
+ ASSERT_TRUE(test_it == test_maps.end());
+}
+
+TEST(libbacktrace, verify_map_remote) {
+ pid_t pid;
+
+ if ((pid = fork()) == 0) {
+ while (true) {
+ }
+ _exit(0);
+ }
+ ASSERT_LT(0, pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ // Wait for the process to get to a stopping point.
+ WaitForStop(pid);
+
+ // The maps should match exactly since the forked process has been paused.
+ VerifyMap(pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, NULL, 0), pid);
+}
+
+#if defined(ENABLE_PSS_TESTS)
+#include "GetPss.h"
+
+#define MAX_LEAK_BYTES 32*1024UL
+
+static void CheckForLeak(pid_t pid, pid_t tid) {
+ // Do a few runs to get the PSS stable.
+ for (size_t i = 0; i < 100; i++) {
+ Backtrace* backtrace = Backtrace::Create(pid, tid);
+ ASSERT_TRUE(backtrace != NULL);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ delete backtrace;
+ }
+ size_t stable_pss = GetPssBytes();
+
+ // Loop enough that even a small leak should be detectable.
+ for (size_t i = 0; i < 4096; i++) {
+ Backtrace* backtrace = Backtrace::Create(pid, tid);
+ ASSERT_TRUE(backtrace != NULL);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ delete backtrace;
+ }
+ size_t new_pss = GetPssBytes();
+ size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss;
+ // As long as the new pss is within a certain amount, consider everything okay.
+ ASSERT_LE(abs_diff, MAX_LEAK_BYTES);
+}
+
+TEST(libbacktrace, check_for_leak_local) {
+ CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD);
+}
+
+TEST(libbacktrace, check_for_leak_local_thread) {
+ thread_t thread_data = { 0, 0, 0 };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, NULL, ThreadLevelRun, &thread_data) == 0);
+
+ // Wait up to 2 seconds for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ CheckForLeak(BACKTRACE_CURRENT_PROCESS, thread_data.tid);
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+
+ ASSERT_TRUE(pthread_join(thread, NULL) == 0);
+}
+
+TEST(libbacktrace, check_for_leak_remote) {
+ pid_t pid;
+
+ if ((pid = fork()) == 0) {
+ while (true) {
+ }
+ _exit(0);
+ }
+ ASSERT_LT(0, pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ // Wait for the process to get to a stopping point.
+ WaitForStop(pid);
+
+ CheckForLeak(pid, BACKTRACE_CURRENT_THREAD);
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, NULL, 0), pid);
+}
+#endif