|  | /* | 
|  | * 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; | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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(); | 
|  | } |