/* * Copyright (C) 2018 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 #include #include #include #include #include #include #include #include #include namespace unwindstack { static std::vector* g_frame_info; static LocalUnwinder* g_unwinder; extern "C" void SignalLocalInnerFunction() { g_unwinder->Unwind(g_frame_info, 256); } extern "C" void SignalLocalMiddleFunction() { SignalLocalInnerFunction(); } extern "C" void SignalLocalOuterFunction() { SignalLocalMiddleFunction(); } static void SignalLocalCallerHandler(int, siginfo_t*, void*) { SignalLocalOuterFunction(); } static std::string ErrorMsg(const std::vector& function_names, const std::vector& frame_info) { std::string unwind; size_t i = 0; for (const auto& frame : frame_info) { unwind += android::base::StringPrintf("#%02zu pc 0x%" PRIx64 " rel_pc 0x%" PRIx64, i++, frame.pc, frame.rel_pc); if (frame.map_info != nullptr) { if (!frame.map_info->name().empty()) { unwind += " "; unwind += frame.map_info->name(); } else { unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start(), frame.map_info->end()); } if (frame.map_info->offset() != 0) { unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset()); } } if (!frame.function_name.empty()) { unwind += " " + frame.function_name; if (frame.function_offset != 0) { unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset); } } unwind += '\n'; } return std::string( "Unwind completed without finding all frames\n" " Looking for function: ") + function_names.front() + "\n" + "Unwind data:\n" + unwind; } // This test assumes that this code is compiled with optimizations turned // off. If this doesn't happen, then all of the calls will be optimized // away. extern "C" void LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) { std::vector frame_info; g_frame_info = &frame_info; g_unwinder = unwinder; std::vector expected_function_names; if (unwind_through_signal) { struct sigaction act, oldact; memset(&act, 0, sizeof(act)); act.sa_sigaction = SignalLocalCallerHandler; act.sa_flags = SA_RESTART | SA_ONSTACK; ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact)); raise(SIGUSR1); ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr)); expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction", "SignalLocalOuterFunction", "SignalLocalMiddleFunction", "SignalLocalInnerFunction"}; } else { ASSERT_TRUE(unwinder->Unwind(&frame_info, 256)); expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"}; } for (auto& frame : frame_info) { if (frame.function_name == expected_function_names.back()) { expected_function_names.pop_back(); if (expected_function_names.empty()) { break; } } } ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info); } extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) { LocalInnerFunction(unwinder, unwind_through_signal); } extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) { LocalMiddleFunction(unwinder, unwind_through_signal); } class LocalUnwinderTest : public ::testing::Test { protected: void SetUp() override { unwinder_.reset(new LocalUnwinder); ASSERT_TRUE(unwinder_->Init()); } std::unique_ptr unwinder_; }; TEST_F(LocalUnwinderTest, local) { LocalOuterFunction(unwinder_.get(), false); } TEST_F(LocalUnwinderTest, local_signal) { LocalOuterFunction(unwinder_.get(), true); } TEST_F(LocalUnwinderTest, local_multiple) { ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false)); ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true)); ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false)); ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true)); } // This test verifies that doing an unwind before and after a dlopen // works. It's verifying that the maps read during the first unwind // do not cause a problem when doing the unwind using the code in // the dlopen'd code. TEST_F(LocalUnwinderTest, unwind_after_dlopen) { // Prime the maps data. ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false)); std::string testlib(testing::internal::GetArgvs()[0]); auto const value = testlib.find_last_of('/'); if (value != std::string::npos) { testlib = testlib.substr(0, value + 1); } else { testlib = ""; } testlib += "libunwindstack_local.so"; void* handle = dlopen(testlib.c_str(), RTLD_NOW); ASSERT_TRUE(handle != nullptr); void (*unwind_function)(void*, void*) = reinterpret_cast(dlsym(handle, "TestlibLevel1")); ASSERT_TRUE(unwind_function != nullptr); std::vector frame_info; unwind_function(unwinder_.get(), &frame_info); ASSERT_EQ(0, dlclose(handle)); std::vector expected_function_names{"TestlibLevel1", "TestlibLevel2", "TestlibLevel3", "TestlibLevel4"}; for (auto& frame : frame_info) { if (frame.function_name == expected_function_names.back()) { expected_function_names.pop_back(); if (expected_function_names.empty()) { break; } } } ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info); } } // namespace unwindstack