/* * Copyright (C) 2013 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. */ /* * Contributed by: Intel Corporation */ #include <gtest/gtest.h> #include <dlfcn.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <unwind.h> #include "ScopedSignalHandler.h" #define noinline __attribute__((__noinline__)) #define __unused __attribute__((__unused__)) _Unwind_Reason_Code FrameCounter(_Unwind_Context* ctx __unused, void* arg) { int* count_ptr = reinterpret_cast<int*>(arg); #if SHOW_FRAME_LOCATIONS void* ip = reinterpret_cast<void*>(_Unwind_GetIP(ctx)); const char* symbol = "<unknown>"; int offset = 0; Dl_info info; memset(&info, 0, sizeof(info)); if (dladdr(ip, &info) != 0) { symbol = info.dli_sname; if (info.dli_saddr != nullptr) { offset = static_cast<int>(reinterpret_cast<char*>(ip) - reinterpret_cast<char*>(info.dli_saddr)); } } fprintf(stderr, " #%02d %p %s%+d (%s)\n", *count_ptr, ip, symbol, offset, info.dli_fname ? info.dli_fname : "??"); fflush(stderr); #endif ++*count_ptr; return _URC_NO_REASON; } static int noinline unwind_one_frame_deeper() { int count = 0; _Unwind_Backtrace(FrameCounter, &count); return count; } TEST(stack_unwinding, easy) { int count = 0; _Unwind_Backtrace(FrameCounter, &count); int deeper_count = unwind_one_frame_deeper(); ASSERT_EQ(count + 1, deeper_count); } struct UnwindData { volatile bool signal_handler_complete = false; int expected_frame_count = 0; int handler_frame_count = 0; int handler_one_deeper_frame_count = 0; }; static UnwindData g_unwind_data; static void noinline UnwindSignalHandler(int) { _Unwind_Backtrace(FrameCounter, &g_unwind_data.handler_frame_count); g_unwind_data.handler_one_deeper_frame_count = unwind_one_frame_deeper(); g_unwind_data.signal_handler_complete = true; } static void verify_unwind_data(const UnwindData& unwind_data) { // In order to avoid a false positive, the caller must have at least 2 frames // outside of the signal handler. This avoids a case where the only frame // right after the signal handler winds up being garbage. EXPECT_GT(unwind_data.handler_frame_count, unwind_data.expected_frame_count + 1); EXPECT_EQ(unwind_data.handler_frame_count + 1, unwind_data.handler_one_deeper_frame_count); } static void noinline UnwindTest() { g_unwind_data = {}; _Unwind_Backtrace(FrameCounter, &g_unwind_data.expected_frame_count); ASSERT_LE(2, g_unwind_data.expected_frame_count) << "The current call must contain at least 2 frames for the test to be valid."; ASSERT_EQ(0, kill(getpid(), SIGUSR1)); while (!g_unwind_data.signal_handler_complete) {} verify_unwind_data(g_unwind_data); } TEST(stack_unwinding, unwind_through_signal_frame) { ScopedSignalHandler ssh(SIGUSR1, UnwindSignalHandler); UnwindTest(); } // On LP32, the SA_SIGINFO flag gets you __restore_rt instead of __restore. TEST(stack_unwinding, unwind_through_signal_frame_SA_SIGINFO) { ScopedSignalHandler ssh(SIGUSR1, UnwindSignalHandler, SA_SIGINFO); UnwindTest(); }