| /* | |
| * 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 "SignalUtils.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 = {}; | |
| 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; | |
| } | |
| static void UnwindTest() { | |
| int count = 0; | |
| _Unwind_Backtrace(FrameCounter, &count); | |
| int deeper_count = unwind_one_frame_deeper(); | |
| ASSERT_EQ(count + 1, deeper_count); | |
| } | |
| TEST(stack_unwinding, easy) { | |
| UnwindTest(); | |
| } | |
| TEST(stack_unwinding, thread) { | |
| pthread_t thread; | |
| ASSERT_EQ(0, pthread_create(&thread, nullptr, [](void*) -> void* { | |
| UnwindTest(); | |
| return nullptr; | |
| }, nullptr)); | |
| void *retval; | |
| ASSERT_EQ(0, pthread_join(thread, &retval)); | |
| EXPECT_EQ(nullptr, retval); | |
| } | |
| 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 SignalUnwindTest() { | |
| 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); | |
| SignalUnwindTest(); | |
| } | |
| // 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); | |
| SignalUnwindTest(); | |
| } |