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