diff options
Diffstat (limited to 'libbacktrace/backtrace_test.cpp')
-rw-r--r-- | libbacktrace/backtrace_test.cpp | 203 |
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 |