diff options
Diffstat (limited to 'libbacktrace/backtrace_test.cpp')
-rw-r--r-- | libbacktrace/backtrace_test.cpp | 439 |
1 files changed, 331 insertions, 108 deletions
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp index 8002ed6..4af6592 100644 --- a/libbacktrace/backtrace_test.cpp +++ b/libbacktrace/backtrace_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#define _GNU_SOURCE 1 #include <dirent.h> #include <errno.h> #include <inttypes.h> @@ -31,15 +32,16 @@ #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> -#include <UniquePtr.h> // For the THREAD_SIGNAL definition. -#include "BacktraceThread.h" +#include "BacktraceCurrent.h" #include <cutils/atomic.h> #include <gtest/gtest.h> #include <algorithm> +#include <memory> +#include <string> #include <vector> #include "thread_utils.h" @@ -60,6 +62,7 @@ struct thread_t { pid_t tid; int32_t state; pthread_t threadId; + void* data; }; struct dump_thread_t { @@ -82,15 +85,16 @@ uint64_t NanoTime() { return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec); } -void DumpFrames(Backtrace* backtrace) { +std::string DumpFrames(Backtrace* backtrace) { if (backtrace->NumFrames() == 0) { - printf(" No frames to dump\n"); - return; + return " No frames to dump.\n"; } + std::string frame; for (size_t i = 0; i < backtrace->NumFrames(); i++) { - printf(" %s\n", backtrace->FormatFrameData(i).c_str()); + frame += " " + backtrace->FormatFrameData(i) + '\n'; } + return frame; } void WaitForStop(pid_t pid) { @@ -120,8 +124,10 @@ bool ReadyLevelBacktrace(Backtrace* backtrace) { } void VerifyLevelDump(Backtrace* backtrace) { - ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0)); - ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES)); + ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0)) + << DumpFrames(backtrace); + ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES)) + << DumpFrames(backtrace); // Look through the frames starting at the highest to find the // frame we want. @@ -132,19 +138,23 @@ void VerifyLevelDump(Backtrace* backtrace) { break; } } - ASSERT_LT(static_cast<size_t>(0), frame_num); - ASSERT_LE(static_cast<size_t>(3), frame_num); - - ASSERT_EQ(backtrace->GetFrame(frame_num)->func_name, "test_level_one"); - ASSERT_EQ(backtrace->GetFrame(frame_num-1)->func_name, "test_level_two"); - ASSERT_EQ(backtrace->GetFrame(frame_num-2)->func_name, "test_level_three"); - ASSERT_EQ(backtrace->GetFrame(frame_num-3)->func_name, "test_level_four"); + ASSERT_LT(static_cast<size_t>(0), frame_num) << DumpFrames(backtrace); + ASSERT_LE(static_cast<size_t>(3), frame_num) << DumpFrames(backtrace); + + ASSERT_EQ(backtrace->GetFrame(frame_num)->func_name, "test_level_one") + << DumpFrames(backtrace); + ASSERT_EQ(backtrace->GetFrame(frame_num-1)->func_name, "test_level_two") + << DumpFrames(backtrace); + ASSERT_EQ(backtrace->GetFrame(frame_num-2)->func_name, "test_level_three") + << DumpFrames(backtrace); + ASSERT_EQ(backtrace->GetFrame(frame_num-3)->func_name, "test_level_four") + << DumpFrames(backtrace); } void VerifyLevelBacktrace(void*) { - UniquePtr<Backtrace> backtrace( + std::unique_ptr<Backtrace> backtrace( Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); - ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace.get() != nullptr); ASSERT_TRUE(backtrace->Unwind(0)); VerifyLevelDump(backtrace.get()); @@ -155,16 +165,17 @@ bool ReadyMaxBacktrace(Backtrace* backtrace) { } void VerifyMaxDump(Backtrace* backtrace) { - ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES)); + ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES)) + << DumpFrames(backtrace); // Verify that the last frame is our recursive call. - ASSERT_EQ(backtrace->GetFrame(MAX_BACKTRACE_FRAMES-1)->func_name, - "test_recursive_call"); + ASSERT_EQ(backtrace->GetFrame(MAX_BACKTRACE_FRAMES-1)->func_name, "test_recursive_call") + << DumpFrames(backtrace); } void VerifyMaxBacktrace(void*) { - UniquePtr<Backtrace> backtrace( + std::unique_ptr<Backtrace> backtrace( Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); - ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace.get() != nullptr); ASSERT_TRUE(backtrace->Unwind(0)); VerifyMaxDump(backtrace.get()); @@ -180,8 +191,8 @@ void ThreadSetState(void* data) { } void VerifyThreadTest(pid_t tid, void (*VerifyFunc)(Backtrace*)) { - UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), tid)); - ASSERT_TRUE(backtrace.get() != NULL); + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), tid)); + ASSERT_TRUE(backtrace.get() != nullptr); ASSERT_TRUE(backtrace->Unwind(0)); VerifyFunc(backtrace.get()); @@ -197,18 +208,38 @@ bool WaitForNonZero(int32_t* value, uint64_t seconds) { return false; } +TEST(libbacktrace, local_no_unwind_frames) { + // Verify that a local unwind does not include any frames within + // libunwind or libbacktrace. + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid())); + ASSERT_TRUE(backtrace.get() != nullptr); + ASSERT_TRUE(backtrace->Unwind(0)); + + ASSERT_TRUE(backtrace->NumFrames() != 0); + for (const auto& frame : *backtrace ) { + if (BacktraceMap::IsValid(frame.map)) { + const std::string name = basename(frame.map.name.c_str()); + ASSERT_TRUE(name != "libunwind.so" && name != "libbacktrace.so") + << DumpFrames(backtrace.get()); + } + break; + } +} + TEST(libbacktrace, local_trace) { - ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, NULL), 0); + ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0); } void VerifyIgnoreFrames( Backtrace* bt_all, Backtrace* bt_ign1, Backtrace* bt_ign2, const char* cur_proc) { - EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1); - EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2); + EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1) + << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 1 backtrace:\n" << DumpFrames(bt_ign1); + EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2) + << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 2 backtrace:\n" << DumpFrames(bt_ign2); // Check all of the frames are the same > the current frame. - bool check = (cur_proc == NULL); + bool check = (cur_proc == nullptr); for (size_t i = 0; i < bt_ign2->NumFrames(); i++) { if (check) { EXPECT_EQ(bt_ign2->GetFrame(i)->pc, bt_ign1->GetFrame(i+1)->pc); @@ -226,30 +257,30 @@ void VerifyIgnoreFrames( } void VerifyLevelIgnoreFrames(void*) { - UniquePtr<Backtrace> all( + std::unique_ptr<Backtrace> all( Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); - ASSERT_TRUE(all.get() != NULL); + ASSERT_TRUE(all.get() != nullptr); ASSERT_TRUE(all->Unwind(0)); - UniquePtr<Backtrace> ign1( + std::unique_ptr<Backtrace> ign1( Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); - ASSERT_TRUE(ign1.get() != NULL); + ASSERT_TRUE(ign1.get() != nullptr); ASSERT_TRUE(ign1->Unwind(1)); - UniquePtr<Backtrace> ign2( + std::unique_ptr<Backtrace> ign2( Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); - ASSERT_TRUE(ign2.get() != NULL); + ASSERT_TRUE(ign2.get() != nullptr); ASSERT_TRUE(ign2->Unwind(2)); VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames"); } TEST(libbacktrace, local_trace_ignore_frames) { - ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, NULL), 0); + ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0); } TEST(libbacktrace, local_max_trace) { - ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, NULL), 0); + ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, nullptr), 0); } void VerifyProcTest(pid_t pid, pid_t tid, bool share_map, @@ -263,35 +294,38 @@ void VerifyProcTest(pid_t pid, pid_t tid, bool share_map, } uint64_t start = NanoTime(); bool verified = false; + std::string last_dump; do { usleep(US_PER_MSEC); if (ptrace(PTRACE_ATTACH, ptrace_tid, 0, 0) == 0) { // Wait for the process to get to a stopping point. WaitForStop(ptrace_tid); - UniquePtr<BacktraceMap> map; + std::unique_ptr<BacktraceMap> map; if (share_map) { map.reset(BacktraceMap::Create(pid)); } - UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get())); + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get())); + ASSERT_TRUE(backtrace.get() != nullptr); ASSERT_TRUE(backtrace->Unwind(0)); - ASSERT_TRUE(backtrace.get() != NULL); if (ReadyFunc(backtrace.get())) { VerifyFunc(backtrace.get()); verified = true; + } else { + last_dump = DumpFrames(backtrace.get()); } ASSERT_TRUE(ptrace(PTRACE_DETACH, ptrace_tid, 0, 0) == 0); } // If 5 seconds have passed, then we are done. } while (!verified && (NanoTime() - start) <= 5 * NS_PER_SEC); - ASSERT_TRUE(verified); + ASSERT_TRUE(verified) << "Last backtrace:\n" << last_dump; } TEST(libbacktrace, ptrace_trace) { pid_t pid; if ((pid = fork()) == 0) { - ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); + ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0); _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump); @@ -304,7 +338,7 @@ TEST(libbacktrace, ptrace_trace) { 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); + ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0); _exit(1); } @@ -318,7 +352,7 @@ TEST(libbacktrace, ptrace_trace_shared_map) { TEST(libbacktrace, ptrace_max_trace) { pid_t pid; if ((pid = fork()) == 0) { - ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0); + ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, nullptr, nullptr), 0); _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump); @@ -329,21 +363,21 @@ TEST(libbacktrace, ptrace_max_trace) { } void VerifyProcessIgnoreFrames(Backtrace* bt_all) { - UniquePtr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD)); - ASSERT_TRUE(ign1.get() != NULL); + std::unique_ptr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(ign1.get() != nullptr); ASSERT_TRUE(ign1->Unwind(1)); - UniquePtr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD)); - ASSERT_TRUE(ign2.get() != NULL); + std::unique_ptr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(ign2.get() != nullptr); ASSERT_TRUE(ign2->Unwind(2)); - VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), NULL); + VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr); } TEST(libbacktrace, ptrace_ignore_frames) { pid_t pid; if ((pid = fork()) == 0) { - ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); + ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0); _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames); @@ -355,8 +389,8 @@ TEST(libbacktrace, ptrace_ignore_frames) { // Create a process with multiple threads and dump all of the threads. void* PtraceThreadLevelRun(void*) { - EXPECT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - return NULL; + EXPECT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0); + return nullptr; } void GetThreads(pid_t pid, std::vector<pid_t>* threads) { @@ -365,9 +399,9 @@ void GetThreads(pid_t pid, std::vector<pid_t>* threads) { snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); DIR* tasks_dir = opendir(task_path); - ASSERT_TRUE(tasks_dir != NULL); + ASSERT_TRUE(tasks_dir != nullptr); struct dirent* entry; - while ((entry = readdir(tasks_dir)) != NULL) { + while ((entry = readdir(tasks_dir)) != nullptr) { char* end; pid_t tid = strtoul(entry->d_name, &end, 10); if (*end == '\0') { @@ -386,9 +420,9 @@ TEST(libbacktrace, ptrace_threads) { pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_t thread; - ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0); + ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, nullptr) == 0); } - ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); + ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0); _exit(1); } @@ -420,27 +454,27 @@ TEST(libbacktrace, ptrace_threads) { } void VerifyLevelThread(void*) { - UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid())); - ASSERT_TRUE(backtrace.get() != NULL); + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid())); + ASSERT_TRUE(backtrace.get() != nullptr); ASSERT_TRUE(backtrace->Unwind(0)); VerifyLevelDump(backtrace.get()); } TEST(libbacktrace, thread_current_level) { - ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, NULL), 0); + ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, nullptr), 0); } void VerifyMaxThread(void*) { - UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid())); - ASSERT_TRUE(backtrace.get() != NULL); + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid())); + ASSERT_TRUE(backtrace.get() != nullptr); ASSERT_TRUE(backtrace->Unwind(0)); VerifyMaxDump(backtrace.get()); } TEST(libbacktrace, thread_current_max) { - ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, NULL), 0); + ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, nullptr), 0); } void* ThreadLevelRun(void* data) { @@ -448,7 +482,7 @@ void* ThreadLevelRun(void* data) { thread->tid = gettid(); EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0); - return NULL; + return nullptr; } TEST(libbacktrace, thread_level_trace) { @@ -456,7 +490,7 @@ TEST(libbacktrace, thread_level_trace) { pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - thread_t thread_data = { 0, 0, 0 }; + thread_t thread_data = { 0, 0, 0, nullptr }; pthread_t thread; ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0); @@ -471,10 +505,10 @@ TEST(libbacktrace, thread_level_trace) { // Save the current signal action and make sure it is restored afterwards. struct sigaction cur_action; - ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &cur_action) == 0); + ASSERT_TRUE(sigaction(THREAD_SIGNAL, nullptr, &cur_action) == 0); - UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid)); - ASSERT_TRUE(backtrace.get() != NULL); + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(backtrace.get() != nullptr); ASSERT_TRUE(backtrace->Unwind(0)); VerifyLevelDump(backtrace.get()); @@ -484,14 +518,18 @@ TEST(libbacktrace, thread_level_trace) { // Verify that the old action was restored. struct sigaction new_action; - ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &new_action) == 0); + ASSERT_TRUE(sigaction(THREAD_SIGNAL, nullptr, &new_action) == 0); EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction); // The SA_RESTORER flag gets set behind our back, so a direct comparison // doesn't work unless we mask the value off. Mips doesn't have this // flag, so skip this on that platform. -#ifdef SA_RESTORER +#if defined(SA_RESTORER) cur_action.sa_flags &= ~SA_RESTORER; new_action.sa_flags &= ~SA_RESTORER; +#elif defined(__GLIBC__) + // Our host compiler doesn't appear to define this flag for some reason. + cur_action.sa_flags &= ~0x04000000; + new_action.sa_flags &= ~0x04000000; #endif EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags); } @@ -501,26 +539,26 @@ TEST(libbacktrace, thread_ignore_frames) { pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - thread_t thread_data = { 0, 0, 0 }; + thread_t thread_data = { 0, 0, 0, nullptr }; pthread_t thread; ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0); // Wait up to 2 seconds for the tid to be set. ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); - UniquePtr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid)); - ASSERT_TRUE(all.get() != NULL); + std::unique_ptr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(all.get() != nullptr); ASSERT_TRUE(all->Unwind(0)); - UniquePtr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid)); - ASSERT_TRUE(ign1.get() != NULL); + std::unique_ptr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(ign1.get() != nullptr); ASSERT_TRUE(ign1->Unwind(1)); - UniquePtr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid)); - ASSERT_TRUE(ign2.get() != NULL); + std::unique_ptr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(ign2.get() != nullptr); ASSERT_TRUE(ign2->Unwind(2)); - VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), NULL); + VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), nullptr); // Tell the thread to exit its infinite loop. android_atomic_acquire_store(0, &thread_data.state); @@ -531,7 +569,7 @@ void* ThreadMaxRun(void* data) { thread->tid = gettid(); EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0); - return NULL; + return nullptr; } TEST(libbacktrace, thread_max_trace) { @@ -539,15 +577,15 @@ TEST(libbacktrace, thread_max_trace) { pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - thread_t thread_data = { 0, 0, 0 }; + thread_t thread_data = { 0, 0, 0, nullptr }; pthread_t thread; ASSERT_TRUE(pthread_create(&thread, &attr, ThreadMaxRun, &thread_data) == 0); // Wait for the tid to be set. ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); - UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid)); - ASSERT_TRUE(backtrace.get() != NULL); + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(backtrace.get() != nullptr); ASSERT_TRUE(backtrace->Unwind(0)); VerifyMaxDump(backtrace.get()); @@ -570,7 +608,7 @@ void* ThreadDump(void* data) { android_atomic_acquire_store(1, &dump->done); - return NULL; + return nullptr; } TEST(libbacktrace, thread_multiple_dump) { @@ -614,11 +652,11 @@ TEST(libbacktrace, thread_multiple_dump) { // Tell the runner thread to exit its infinite loop. android_atomic_acquire_store(0, &runners[i].state); - ASSERT_TRUE(dumpers[i].backtrace != NULL); + ASSERT_TRUE(dumpers[i].backtrace != nullptr); VerifyMaxDump(dumpers[i].backtrace); delete dumpers[i].backtrace; - dumpers[i].backtrace = NULL; + dumpers[i].backtrace = nullptr; } } @@ -654,11 +692,11 @@ TEST(libbacktrace, thread_multiple_dump_same_thread) { for (size_t i = 0; i < NUM_THREADS; i++) { ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 30)); - ASSERT_TRUE(dumpers[i].backtrace != NULL); + ASSERT_TRUE(dumpers[i].backtrace != nullptr); VerifyMaxDump(dumpers[i].backtrace); delete dumpers[i].backtrace; - dumpers[i].backtrace = NULL; + dumpers[i].backtrace = nullptr; } // Tell the runner thread to exit its infinite loop. @@ -673,37 +711,54 @@ TEST(libbacktrace, simultaneous_maps) { BacktraceMap* map3 = BacktraceMap::Create(getpid()); Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1); + ASSERT_TRUE(back1 != nullptr); EXPECT_TRUE(back1->Unwind(0)); delete back1; delete map1; Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2); + ASSERT_TRUE(back2 != nullptr); EXPECT_TRUE(back2->Unwind(0)); delete back2; delete map2; Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3); + ASSERT_TRUE(back3 != nullptr); EXPECT_TRUE(back3->Unwind(0)); delete back3; delete map3; } +TEST(libbacktrace, fillin_erases) { + BacktraceMap* back_map = BacktraceMap::Create(getpid()); + + backtrace_map_t map; + + map.start = 1; + map.end = 3; + map.flags = 1; + map.name = "Initialized"; + back_map->FillIn(0, &map); + delete back_map; + + ASSERT_FALSE(BacktraceMap::IsValid(map)); + ASSERT_EQ(static_cast<uintptr_t>(0), map.start); + ASSERT_EQ(static_cast<uintptr_t>(0), map.end); + ASSERT_EQ(0, map.flags); + ASSERT_EQ("", map.name); +} + TEST(libbacktrace, format_test) { - UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD)); - ASSERT_TRUE(backtrace.get() != NULL); + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(backtrace.get() != nullptr); backtrace_frame_data_t frame; frame.num = 1; frame.pc = 2; frame.sp = 0; frame.stack_size = 0; - frame.map = NULL; frame.func_offset = 0; - backtrace_map_t map; - map.start = 0; - map.end = 0; - // Check no map set. frame.num = 1; #if defined(__LP64__) @@ -714,8 +769,8 @@ TEST(libbacktrace, format_test) { backtrace->FormatFrameData(&frame)); // Check map name empty, but exists. - frame.map = ↦ - map.start = 1; + frame.map.start = 1; + frame.map.end = 1; #if defined(__LP64__) EXPECT_EQ("#01 pc 0000000000000001 <unknown>", #else @@ -726,9 +781,9 @@ TEST(libbacktrace, format_test) { // Check relative pc is set and map name is set. frame.pc = 0x12345679; - frame.map = ↦ - map.name = "MapFake"; - map.start = 1; + frame.map.name = "MapFake"; + frame.map.start = 1; + frame.map.end = 1; #if defined(__LP64__) EXPECT_EQ("#01 pc 0000000012345678 MapFake", #else @@ -764,12 +819,12 @@ bool map_sort(map_test_t i, map_test_t j) { return i.start < j.start; } -static void VerifyMap(pid_t pid) { +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); + ASSERT_TRUE(map_file != nullptr); std::vector<map_test_t> test_maps; while (fgets(buffer, sizeof(buffer), map_file)) { map_test_t map; @@ -779,7 +834,7 @@ static void VerifyMap(pid_t pid) { fclose(map_file); std::sort(test_maps.begin(), test_maps.end(), map_sort); - UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid)); + std::unique_ptr<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(); @@ -813,7 +868,173 @@ TEST(libbacktrace, verify_map_remote) { ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); kill(pid, SIGKILL); - ASSERT_EQ(waitpid(pid, NULL, 0), pid); + ASSERT_EQ(waitpid(pid, nullptr, 0), pid); +} + +void* ThreadReadTest(void* data) { + thread_t* thread_data = reinterpret_cast<thread_t*>(data); + + thread_data->tid = gettid(); + + // Create two map pages. + // Mark the second page as not-readable. + size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE)); + uint8_t* memory; + if (posix_memalign(reinterpret_cast<void**>(&memory), pagesize, 2 * pagesize) != 0) { + return reinterpret_cast<void*>(-1); + } + + if (mprotect(&memory[pagesize], pagesize, PROT_NONE) != 0) { + return reinterpret_cast<void*>(-1); + } + + // Set up a simple pattern in memory. + for (size_t i = 0; i < pagesize; i++) { + memory[i] = i; + } + + thread_data->data = memory; + + // Tell the caller it's okay to start reading memory. + android_atomic_acquire_store(1, &thread_data->state); + + // Loop waiting for the caller to finish reading the memory. + while (thread_data->state) { + } + + // Re-enable read-write on the page so that we don't crash if we try + // and access data on this page when freeing the memory. + if (mprotect(&memory[pagesize], pagesize, PROT_READ | PROT_WRITE) != 0) { + return reinterpret_cast<void*>(-1); + } + free(memory); + + android_atomic_acquire_store(1, &thread_data->state); + + return nullptr; +} + +void RunReadTest(Backtrace* backtrace, uintptr_t read_addr) { + size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE)); + + // Create a page of data to use to do quick compares. + uint8_t* expected = new uint8_t[pagesize]; + for (size_t i = 0; i < pagesize; i++) { + expected[i] = i; + } + uint8_t* data = new uint8_t[2*pagesize]; + // Verify that we can only read one page worth of data. + size_t bytes_read = backtrace->Read(read_addr, data, 2 * pagesize); + ASSERT_EQ(pagesize, bytes_read); + ASSERT_TRUE(memcmp(data, expected, pagesize) == 0); + + // Verify unaligned reads. + for (size_t i = 1; i < sizeof(word_t); i++) { + bytes_read = backtrace->Read(read_addr + i, data, 2 * sizeof(word_t)); + ASSERT_EQ(2 * sizeof(word_t), bytes_read); + ASSERT_TRUE(memcmp(data, &expected[i], 2 * sizeof(word_t)) == 0) + << "Offset at " << i << " failed"; + } + delete data; + delete expected; +} + +TEST(libbacktrace, thread_read) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_t thread; + thread_t thread_data = { 0, 0, 0, nullptr }; + ASSERT_TRUE(pthread_create(&thread, &attr, ThreadReadTest, &thread_data) == 0); + + ASSERT_TRUE(WaitForNonZero(&thread_data.state, 10)); + + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(backtrace.get() != nullptr); + + RunReadTest(backtrace.get(), reinterpret_cast<uintptr_t>(thread_data.data)); + + android_atomic_acquire_store(0, &thread_data.state); + + ASSERT_TRUE(WaitForNonZero(&thread_data.state, 10)); +} + +volatile uintptr_t g_ready = 0; +volatile uintptr_t g_addr = 0; + +void ForkedReadTest() { + // Create two map pages. + size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE)); + uint8_t* memory; + if (posix_memalign(reinterpret_cast<void**>(&memory), pagesize, 2 * pagesize) != 0) { + perror("Failed to allocate memory\n"); + exit(1); + } + + // Mark the second page as not-readable. + if (mprotect(&memory[pagesize], pagesize, PROT_NONE) != 0) { + perror("Failed to mprotect memory\n"); + exit(1); + } + + // Set up a simple pattern in memory. + for (size_t i = 0; i < pagesize; i++) { + memory[i] = i; + } + + g_addr = reinterpret_cast<uintptr_t>(memory); + g_ready = 1; + + while (1) { + usleep(US_PER_MSEC); + } +} + +TEST(libbacktrace, process_read) { + pid_t pid; + if ((pid = fork()) == 0) { + ForkedReadTest(); + exit(0); + } + ASSERT_NE(-1, pid); + + bool test_executed = false; + uint64_t start = NanoTime(); + while (1) { + if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) { + WaitForStop(pid); + + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid)); + ASSERT_TRUE(backtrace.get() != nullptr); + + uintptr_t read_addr; + size_t bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(&g_ready), + reinterpret_cast<uint8_t*>(&read_addr), + sizeof(uintptr_t)); + ASSERT_EQ(sizeof(uintptr_t), bytes_read); + if (read_addr) { + // The forked process is ready to be read. + bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(&g_addr), + reinterpret_cast<uint8_t*>(&read_addr), + sizeof(uintptr_t)); + ASSERT_EQ(sizeof(uintptr_t), bytes_read); + + RunReadTest(backtrace.get(), read_addr); + + test_executed = true; + break; + } + ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); + } + if ((NanoTime() - start) > 5 * NS_PER_SEC) { + break; + } + usleep(US_PER_MSEC); + } + kill(pid, SIGKILL); + ASSERT_EQ(waitpid(pid, nullptr, 0), pid); + + ASSERT_TRUE(test_executed); } #if defined(ENABLE_PSS_TESTS) @@ -821,24 +1042,26 @@ TEST(libbacktrace, verify_map_remote) { #define MAX_LEAK_BYTES 32*1024UL -static void CheckForLeak(pid_t pid, pid_t tid) { +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 != nullptr); ASSERT_TRUE(backtrace->Unwind(0)); delete backtrace; } size_t stable_pss = GetPssBytes(); + ASSERT_TRUE(stable_pss != 0); // 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 != nullptr); ASSERT_TRUE(backtrace->Unwind(0)); delete backtrace; } size_t new_pss = GetPssBytes(); + ASSERT_TRUE(new_pss != 0); 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); @@ -849,9 +1072,9 @@ TEST(libbacktrace, check_for_leak_local) { } TEST(libbacktrace, check_for_leak_local_thread) { - thread_t thread_data = { 0, 0, 0 }; + thread_t thread_data = { 0, 0, 0, nullptr }; pthread_t thread; - ASSERT_TRUE(pthread_create(&thread, NULL, ThreadLevelRun, &thread_data) == 0); + ASSERT_TRUE(pthread_create(&thread, nullptr, ThreadLevelRun, &thread_data) == 0); // Wait up to 2 seconds for the tid to be set. ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); @@ -861,7 +1084,7 @@ TEST(libbacktrace, check_for_leak_local_thread) { // Tell the thread to exit its infinite loop. android_atomic_acquire_store(0, &thread_data.state); - ASSERT_TRUE(pthread_join(thread, NULL) == 0); + ASSERT_TRUE(pthread_join(thread, nullptr) == 0); } TEST(libbacktrace, check_for_leak_remote) { @@ -884,6 +1107,6 @@ TEST(libbacktrace, check_for_leak_remote) { ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); kill(pid, SIGKILL); - ASSERT_EQ(waitpid(pid, NULL, 0), pid); + ASSERT_EQ(waitpid(pid, nullptr, 0), pid); } #endif |