• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SignalLocalInnerFunction()37 extern "C" void SignalLocalInnerFunction() {
38   g_unwinder->Unwind(g_frame_info, 256);
39 }
40 
SignalLocalMiddleFunction()41 extern "C" void SignalLocalMiddleFunction() {
42   SignalLocalInnerFunction();
43 }
44 
SignalLocalOuterFunction()45 extern "C" void SignalLocalOuterFunction() {
46   SignalLocalMiddleFunction();
47 }
48 
SignalLocalCallerHandler(int,siginfo_t *,void *)49 static void SignalLocalCallerHandler(int, siginfo_t*, void*) {
50   SignalLocalOuterFunction();
51 }
52 
ErrorMsg(const std::vector<const char * > & function_names,const std::vector<LocalFrameData> & frame_info)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 += " ";
63         unwind += frame.map_info->name();
64       } else {
65         unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start(),
66                                               frame.map_info->end());
67       }
68       if (frame.map_info->offset() != 0) {
69         unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset());
70       }
71     }
72     if (!frame.function_name.empty()) {
73       unwind += " " + frame.function_name;
74       if (frame.function_offset != 0) {
75         unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset);
76       }
77     }
78     unwind += '\n';
79   }
80 
81   return std::string(
82              "Unwind completed without finding all frames\n"
83              "  Looking for function: ") +
84          function_names.front() + "\n" + "Unwind data:\n" + unwind;
85 }
86 
87 // This test assumes that this code is compiled with optimizations turned
88 // off. If this doesn't happen, then all of the calls will be optimized
89 // away.
LocalInnerFunction(LocalUnwinder * unwinder,bool unwind_through_signal)90 extern "C" void LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
91   std::vector<LocalFrameData> frame_info;
92   g_frame_info = &frame_info;
93   g_unwinder = unwinder;
94   std::vector<const char*> expected_function_names;
95 
96   if (unwind_through_signal) {
97     struct sigaction act, oldact;
98     memset(&act, 0, sizeof(act));
99     act.sa_sigaction = SignalLocalCallerHandler;
100     act.sa_flags = SA_RESTART | SA_ONSTACK;
101     ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
102 
103     raise(SIGUSR1);
104 
105     ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
106 
107     expected_function_names = {"LocalOuterFunction",        "LocalMiddleFunction",
108                                "LocalInnerFunction",        "SignalLocalOuterFunction",
109                                "SignalLocalMiddleFunction", "SignalLocalInnerFunction"};
110   } else {
111     ASSERT_TRUE(unwinder->Unwind(&frame_info, 256));
112 
113     expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"};
114   }
115 
116   for (auto& frame : frame_info) {
117     if (frame.function_name == expected_function_names.back()) {
118       expected_function_names.pop_back();
119       if (expected_function_names.empty()) {
120         break;
121       }
122     }
123   }
124 
125   ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
126 }
127 
LocalMiddleFunction(LocalUnwinder * unwinder,bool unwind_through_signal)128 extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
129   LocalInnerFunction(unwinder, unwind_through_signal);
130 }
131 
LocalOuterFunction(LocalUnwinder * unwinder,bool unwind_through_signal)132 extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
133   LocalMiddleFunction(unwinder, unwind_through_signal);
134 }
135 
136 class LocalUnwinderTest : public ::testing::Test {
137  protected:
SetUp()138   void SetUp() override {
139     unwinder_.reset(new LocalUnwinder);
140     ASSERT_TRUE(unwinder_->Init());
141   }
142 
143   std::unique_ptr<LocalUnwinder> unwinder_;
144 };
145 
TEST_F(LocalUnwinderTest,local)146 TEST_F(LocalUnwinderTest, local) {
147   LocalOuterFunction(unwinder_.get(), false);
148 }
149 
TEST_F(LocalUnwinderTest,local_signal)150 TEST_F(LocalUnwinderTest, local_signal) {
151   LocalOuterFunction(unwinder_.get(), true);
152 }
153 
TEST_F(LocalUnwinderTest,local_multiple)154 TEST_F(LocalUnwinderTest, local_multiple) {
155   ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
156 
157   ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
158 
159   ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
160 
161   ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
162 }
163 
164 // This test verifies that doing an unwind before and after a dlopen
165 // works. It's verifying that the maps read during the first unwind
166 // do not cause a problem when doing the unwind using the code in
167 // the dlopen'd code.
TEST_F(LocalUnwinderTest,unwind_after_dlopen)168 TEST_F(LocalUnwinderTest, unwind_after_dlopen) {
169   // Prime the maps data.
170   ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
171 
172   std::string testlib(testing::internal::GetArgvs()[0]);
173   auto const value = testlib.find_last_of('/');
174   if (value != std::string::npos) {
175     testlib = testlib.substr(0, value + 1);
176   } else {
177     testlib = "";
178   }
179   testlib += "libunwindstack_local.so";
180 
181   void* handle = dlopen(testlib.c_str(), RTLD_NOW);
182   ASSERT_TRUE(handle != nullptr);
183 
184   void (*unwind_function)(void*, void*) =
185       reinterpret_cast<void (*)(void*, void*)>(dlsym(handle, "TestlibLevel1"));
186   ASSERT_TRUE(unwind_function != nullptr);
187 
188   std::vector<LocalFrameData> frame_info;
189   unwind_function(unwinder_.get(), &frame_info);
190 
191   ASSERT_EQ(0, dlclose(handle));
192 
193   std::vector<const char*> expected_function_names{"TestlibLevel1", "TestlibLevel2",
194                                                    "TestlibLevel3", "TestlibLevel4"};
195 
196   for (auto& frame : frame_info) {
197     if (frame.function_name == expected_function_names.back()) {
198       expected_function_names.pop_back();
199       if (expected_function_names.empty()) {
200         break;
201       }
202     }
203   }
204 
205   ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
206 }
207 
208 }  // namespace unwindstack
209