| Christopher Ferris | ca9a54b | 2018-04-05 11:15:00 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2018 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #include <dlfcn.h> | 
 | 18 | #include <inttypes.h> | 
 | 19 | #include <signal.h> | 
 | 20 | #include <stdint.h> | 
 | 21 |  | 
 | 22 | #include <memory> | 
 | 23 | #include <string> | 
 | 24 | #include <vector> | 
 | 25 |  | 
 | 26 | #include <gtest/gtest.h> | 
 | 27 |  | 
 | 28 | #include <android-base/stringprintf.h> | 
 | 29 |  | 
 | 30 | #include <unwindstack/LocalUnwinder.h> | 
 | 31 |  | 
 | 32 | namespace unwindstack { | 
 | 33 |  | 
 | 34 | static std::vector<LocalFrameData>* g_frame_info; | 
 | 35 | static LocalUnwinder* g_unwinder; | 
 | 36 |  | 
 | 37 | extern "C" void SignalLocalInnerFunction() { | 
 | 38 |   g_unwinder->Unwind(g_frame_info, 256); | 
 | 39 | } | 
 | 40 |  | 
 | 41 | extern "C" void SignalLocalMiddleFunction() { | 
 | 42 |   SignalLocalInnerFunction(); | 
 | 43 | } | 
 | 44 |  | 
 | 45 | extern "C" void SignalLocalOuterFunction() { | 
 | 46 |   SignalLocalMiddleFunction(); | 
 | 47 | } | 
 | 48 |  | 
 | 49 | static void SignalLocalCallerHandler(int, siginfo_t*, void*) { | 
 | 50 |   SignalLocalOuterFunction(); | 
 | 51 | } | 
 | 52 |  | 
 | 53 | static std::string ErrorMsg(const std::vector<const char*>& function_names, | 
 | 54 |                             const std::vector<LocalFrameData>& frame_info) { | 
 | 55 |   std::string unwind; | 
 | 56 |   size_t i = 0; | 
 | 57 |   for (const auto& frame : frame_info) { | 
 | 58 |     unwind += android::base::StringPrintf("#%02zu pc 0x%" PRIx64 " rel_pc 0x%" PRIx64, i++, | 
 | 59 |                                           frame.pc, frame.rel_pc); | 
 | 60 |     if (frame.map_info != nullptr) { | 
 | 61 |       if (!frame.map_info->name.empty()) { | 
 | 62 |         unwind += " " + frame.map_info->name; | 
 | 63 |       } else { | 
 | 64 |         unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start, | 
 | 65 |                                               frame.map_info->end); | 
 | 66 |       } | 
 | 67 |       if (frame.map_info->offset != 0) { | 
 | 68 |         unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset); | 
 | 69 |       } | 
 | 70 |     } | 
 | 71 |     if (!frame.function_name.empty()) { | 
 | 72 |       unwind += " " + frame.function_name; | 
 | 73 |       if (frame.function_offset != 0) { | 
 | 74 |         unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset); | 
 | 75 |       } | 
 | 76 |     } | 
 | 77 |     unwind += '\n'; | 
 | 78 |   } | 
 | 79 |  | 
 | 80 |   return std::string( | 
 | 81 |              "Unwind completed without finding all frames\n" | 
 | 82 |              "  Looking for function: ") + | 
 | 83 |          function_names.front() + "\n" + "Unwind data:\n" + unwind; | 
 | 84 | } | 
 | 85 |  | 
 | 86 | // This test assumes that this code is compiled with optimizations turned | 
 | 87 | // off. If this doesn't happen, then all of the calls will be optimized | 
 | 88 | // away. | 
 | 89 | extern "C" void LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) { | 
 | 90 |   std::vector<LocalFrameData> frame_info; | 
 | 91 |   g_frame_info = &frame_info; | 
 | 92 |   g_unwinder = unwinder; | 
 | 93 |   std::vector<const char*> expected_function_names; | 
 | 94 |  | 
 | 95 |   if (unwind_through_signal) { | 
 | 96 |     struct sigaction act, oldact; | 
 | 97 |     memset(&act, 0, sizeof(act)); | 
 | 98 |     act.sa_sigaction = SignalLocalCallerHandler; | 
 | 99 |     act.sa_flags = SA_RESTART | SA_ONSTACK; | 
 | 100 |     ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact)); | 
 | 101 |  | 
 | 102 |     raise(SIGUSR1); | 
 | 103 |  | 
 | 104 |     ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr)); | 
 | 105 |  | 
 | 106 |     expected_function_names = {"LocalOuterFunction",        "LocalMiddleFunction", | 
 | 107 |                                "LocalInnerFunction",        "SignalLocalOuterFunction", | 
 | 108 |                                "SignalLocalMiddleFunction", "SignalLocalInnerFunction"}; | 
 | 109 |   } else { | 
 | 110 |     ASSERT_TRUE(unwinder->Unwind(&frame_info, 256)); | 
 | 111 |  | 
 | 112 |     expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"}; | 
 | 113 |   } | 
 | 114 |  | 
 | 115 |   for (auto& frame : frame_info) { | 
 | 116 |     if (frame.function_name == expected_function_names.back()) { | 
 | 117 |       expected_function_names.pop_back(); | 
 | 118 |       if (expected_function_names.empty()) { | 
 | 119 |         break; | 
 | 120 |       } | 
 | 121 |     } | 
 | 122 |   } | 
 | 123 |  | 
 | 124 |   ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info); | 
 | 125 | } | 
 | 126 |  | 
 | 127 | extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) { | 
 | 128 |   LocalInnerFunction(unwinder, unwind_through_signal); | 
 | 129 | } | 
 | 130 |  | 
 | 131 | extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) { | 
 | 132 |   LocalMiddleFunction(unwinder, unwind_through_signal); | 
 | 133 | } | 
 | 134 |  | 
 | 135 | class LocalUnwinderTest : public ::testing::Test { | 
 | 136 |  protected: | 
 | 137 |   void SetUp() override { | 
 | 138 |     unwinder_.reset(new LocalUnwinder); | 
 | 139 |     ASSERT_TRUE(unwinder_->Init()); | 
 | 140 |   } | 
 | 141 |  | 
 | 142 |   std::unique_ptr<LocalUnwinder> unwinder_; | 
 | 143 | }; | 
 | 144 |  | 
 | 145 | TEST_F(LocalUnwinderTest, local) { | 
 | 146 |   LocalOuterFunction(unwinder_.get(), false); | 
 | 147 | } | 
 | 148 |  | 
 | 149 | TEST_F(LocalUnwinderTest, local_signal) { | 
 | 150 |   LocalOuterFunction(unwinder_.get(), true); | 
 | 151 | } | 
 | 152 |  | 
 | 153 | TEST_F(LocalUnwinderTest, local_multiple) { | 
 | 154 |   ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false)); | 
 | 155 |  | 
 | 156 |   ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true)); | 
 | 157 |  | 
 | 158 |   ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false)); | 
 | 159 |  | 
 | 160 |   ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true)); | 
 | 161 | } | 
 | 162 |  | 
 | 163 | // This test verifies that doing an unwind before and after a dlopen | 
 | 164 | // works. It's verifying that the maps read during the first unwind | 
 | 165 | // do not cause a problem when doing the unwind using the code in | 
 | 166 | // the dlopen'd code. | 
 | 167 | TEST_F(LocalUnwinderTest, unwind_after_dlopen) { | 
 | 168 |   // Prime the maps data. | 
 | 169 |   ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false)); | 
 | 170 |  | 
 | 171 |   std::string testlib(testing::internal::GetArgvs()[0]); | 
 | 172 |   auto const value = testlib.find_last_of('/'); | 
 | 173 |   if (value == std::string::npos) { | 
 | 174 |     testlib = "../"; | 
 | 175 |   } else { | 
 | 176 |     testlib = testlib.substr(0, value + 1) + "../"; | 
 | 177 |   } | 
 | 178 |   testlib += "libunwindstack_local.so"; | 
 | 179 |  | 
 | 180 |   void* handle = dlopen(testlib.c_str(), RTLD_NOW); | 
 | 181 |   ASSERT_TRUE(handle != nullptr); | 
 | 182 |  | 
 | 183 |   void (*unwind_function)(void*, void*) = | 
 | 184 |       reinterpret_cast<void (*)(void*, void*)>(dlsym(handle, "TestlibLevel1")); | 
 | 185 |   ASSERT_TRUE(unwind_function != nullptr); | 
 | 186 |  | 
 | 187 |   std::vector<LocalFrameData> frame_info; | 
 | 188 |   unwind_function(unwinder_.get(), &frame_info); | 
 | 189 |  | 
 | 190 |   ASSERT_EQ(0, dlclose(handle)); | 
 | 191 |  | 
 | 192 |   std::vector<const char*> expected_function_names{"TestlibLevel1", "TestlibLevel2", | 
 | 193 |                                                    "TestlibLevel3", "TestlibLevel4"}; | 
 | 194 |  | 
 | 195 |   for (auto& frame : frame_info) { | 
 | 196 |     if (frame.function_name == expected_function_names.back()) { | 
 | 197 |       expected_function_names.pop_back(); | 
 | 198 |       if (expected_function_names.empty()) { | 
 | 199 |         break; | 
 | 200 |       } | 
 | 201 |     } | 
 | 202 |   } | 
 | 203 |  | 
 | 204 |   ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info); | 
 | 205 | } | 
 | 206 |  | 
 | 207 | }  // namespace unwindstack |