blob: 56a18cde2a3ce0e776ae5fe81fcaf8f3a4c0f73c [file] [log] [blame]
Christopher Ferrisca9a54b2018-04-05 11:15:00 -07001/*
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
32namespace unwindstack {
33
34static std::vector<LocalFrameData>* g_frame_info;
35static LocalUnwinder* g_unwinder;
36
37extern "C" void SignalLocalInnerFunction() {
38 g_unwinder->Unwind(g_frame_info, 256);
39}
40
41extern "C" void SignalLocalMiddleFunction() {
42 SignalLocalInnerFunction();
43}
44
45extern "C" void SignalLocalOuterFunction() {
46 SignalLocalMiddleFunction();
47}
48
49static void SignalLocalCallerHandler(int, siginfo_t*, void*) {
50 SignalLocalOuterFunction();
51}
52
53static 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.
89extern "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
127extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
128 LocalInnerFunction(unwinder, unwind_through_signal);
129}
130
131extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
132 LocalMiddleFunction(unwinder, unwind_through_signal);
133}
134
135class 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
145TEST_F(LocalUnwinderTest, local) {
146 LocalOuterFunction(unwinder_.get(), false);
147}
148
149TEST_F(LocalUnwinderTest, local_signal) {
150 LocalOuterFunction(unwinder_.get(), true);
151}
152
153TEST_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.
167TEST_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