summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Ferris2018-05-24 13:06:02 -0500
committerGerrit Code Review2018-05-24 13:06:02 -0500
commita6a433a27d85aab489dc78b355bef60f4a6978ca (patch)
tree044f706880d7193f7d7541e6906e4dd1e1ca3585
parent925b410c7f65f88f9483c68bfbdec64c31b92570 (diff)
parentca9a54b2c0381cc59598e8e560be9ad10b7c2e38 (diff)
downloadplatform-system-core-a6a433a27d85aab489dc78b355bef60f4a6978ca.tar.gz
platform-system-core-a6a433a27d85aab489dc78b355bef60f4a6978ca.tar.xz
platform-system-core-a6a433a27d85aab489dc78b355bef60f4a6978ca.zip
Merge "Add a specialized LocalUnwinder object."
-rw-r--r--libunwindstack/Android.bp17
-rw-r--r--libunwindstack/LocalUnwinder.cpp146
-rw-r--r--libunwindstack/Maps.cpp79
-rw-r--r--libunwindstack/include/unwindstack/LocalUnwinder.h86
-rw-r--r--libunwindstack/include/unwindstack/Maps.h13
-rw-r--r--libunwindstack/tests/LocalUnwinderTest.cpp207
-rw-r--r--libunwindstack/tests/TestLocal.cpp39
-rw-r--r--libunwindstack/tests/UnwindTest.cpp7
8 files changed, 589 insertions, 5 deletions
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index a6bf73073..435ed9462 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -62,6 +62,7 @@ cc_library {
62 "MapInfo.cpp", 62 "MapInfo.cpp",
63 "Maps.cpp", 63 "Maps.cpp",
64 "Memory.cpp", 64 "Memory.cpp",
65 "LocalUnwinder.cpp",
65 "Regs.cpp", 66 "Regs.cpp",
66 "RegsArm.cpp", 67 "RegsArm.cpp",
67 "RegsArm64.cpp", 68 "RegsArm64.cpp",
@@ -125,6 +126,21 @@ cc_library {
125//------------------------------------------------------------------------- 126//-------------------------------------------------------------------------
126// Unit Tests 127// Unit Tests
127//------------------------------------------------------------------------- 128//-------------------------------------------------------------------------
129cc_test_library {
130 name: "libunwindstack_local",
131 defaults: ["libunwindstack_flags"],
132 srcs: ["tests/TestLocal.cpp"],
133
134 cflags: [
135 "-O0",
136 "-g",
137 ],
138
139 shared_libs: [
140 "libunwindstack",
141 ],
142}
143
128cc_test { 144cc_test {
129 name: "libunwindstack_test", 145 name: "libunwindstack_test",
130 defaults: ["libunwindstack_flags"], 146 defaults: ["libunwindstack_flags"],
@@ -151,6 +167,7 @@ cc_test {
151 "tests/ElfTest.cpp", 167 "tests/ElfTest.cpp",
152 "tests/ElfTestUtils.cpp", 168 "tests/ElfTestUtils.cpp",
153 "tests/JitDebugTest.cpp", 169 "tests/JitDebugTest.cpp",
170 "tests/LocalUnwinderTest.cpp",
154 "tests/LogFake.cpp", 171 "tests/LogFake.cpp",
155 "tests/MapInfoGetElfTest.cpp", 172 "tests/MapInfoGetElfTest.cpp",
156 "tests/MapInfoGetLoadBiasTest.cpp", 173 "tests/MapInfoGetLoadBiasTest.cpp",
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
new file mode 100644
index 000000000..952b332f0
--- /dev/null
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -0,0 +1,146 @@
1/*
2 * Copyright (C) 2018 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <pthread.h>
30#include <stdint.h>
31
32#include <memory>
33#include <string>
34#include <vector>
35
36#include <unwindstack/Elf.h>
37#include <unwindstack/LocalUnwinder.h>
38#include <unwindstack/MapInfo.h>
39#include <unwindstack/Maps.h>
40#include <unwindstack/Memory.h>
41#include <unwindstack/Regs.h>
42#include <unwindstack/RegsGetLocal.h>
43
44namespace unwindstack {
45
46bool LocalUnwinder::Init() {
47 pthread_rwlock_init(&maps_rwlock_, nullptr);
48
49 // Create the maps.
50 maps_.reset(new unwindstack::LocalUpdatableMaps());
51 if (!maps_->Parse()) {
52 maps_.reset();
53 return false;
54 }
55
56 process_memory_ = unwindstack::Memory::CreateProcessMemory(getpid());
57
58 return true;
59}
60
61bool LocalUnwinder::ShouldSkipLibrary(const std::string& map_name) {
62 for (const std::string& skip_library : skip_libraries_) {
63 if (skip_library == map_name) {
64 return true;
65 }
66 }
67 return false;
68}
69
70MapInfo* LocalUnwinder::GetMapInfo(uint64_t pc) {
71 pthread_rwlock_rdlock(&maps_rwlock_);
72 MapInfo* map_info = maps_->Find(pc);
73 pthread_rwlock_unlock(&maps_rwlock_);
74
75 if (map_info == nullptr) {
76 pthread_rwlock_wrlock(&maps_rwlock_);
77 // This is guaranteed not to invalidate any previous MapInfo objects so
78 // we don't need to worry about any MapInfo* values already in use.
79 if (maps_->Reparse()) {
80 map_info = maps_->Find(pc);
81 }
82 pthread_rwlock_unlock(&maps_rwlock_);
83 }
84
85 return map_info;
86}
87
88bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
89 std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
90 unwindstack::RegsGetLocal(regs.get());
91
92 size_t num_frames = 0;
93 bool adjust_pc = false;
94 while (true) {
95 uint64_t cur_pc = regs->pc();
96 uint64_t cur_sp = regs->sp();
97
98 MapInfo* map_info = GetMapInfo(cur_pc);
99 if (map_info == nullptr) {
100 break;
101 }
102
103 Elf* elf = map_info->GetElf(process_memory_, true);
104 uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
105 uint64_t step_pc = rel_pc;
106 uint64_t pc_adjustment;
107 if (adjust_pc) {
108 pc_adjustment = regs->GetPcAdjustment(rel_pc, elf);
109 } else {
110 pc_adjustment = 0;
111 }
112 step_pc -= pc_adjustment;
113 // Skip any locations that are within this library.
114 if (num_frames != 0 || !ShouldSkipLibrary(map_info->name)) {
115 // Add frame information.
116 std::string func_name;
117 uint64_t func_offset;
118 if (elf->GetFunctionName(rel_pc, &func_name, &func_offset)) {
119 frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment,
120 func_name, func_offset);
121 } else {
122 frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, "", 0);
123 }
124 num_frames++;
125 }
126 if (!elf->valid()) {
127 break;
128 }
129 if (frame_info->size() == max_frames) {
130 break;
131 }
132
133 adjust_pc = true;
134 bool finished;
135 if (!elf->Step(rel_pc, step_pc, regs.get(), process_memory_.get(), &finished) || finished) {
136 break;
137 }
138 // pc and sp are the same, terminate the unwind.
139 if (cur_pc == regs->pc() && cur_sp == regs->sp()) {
140 break;
141 }
142 }
143 return num_frames != 0;
144}
145
146} // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index bb682ea69..e676a5a97 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -105,4 +105,83 @@ const std::string RemoteMaps::GetMapsFile() const {
105 return "/proc/" + std::to_string(pid_) + "/maps"; 105 return "/proc/" + std::to_string(pid_) + "/maps";
106} 106}
107 107
108const std::string LocalUpdatableMaps::GetMapsFile() const {
109 return "/proc/self/maps";
110}
111
112bool LocalUpdatableMaps::Reparse() {
113 // New maps will be added at the end without deleting the old ones.
114 size_t last_map_idx = maps_.size();
115 if (!Parse()) {
116 // Delete any maps added by the Parse call.
117 for (size_t i = last_map_idx; i < maps_.size(); i++) {
118 delete maps_[i];
119 }
120 maps_.resize(last_map_idx);
121 return false;
122 }
123
124 size_t total_entries = maps_.size();
125 size_t search_map_idx = 0;
126 for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
127 MapInfo* new_map_info = maps_[new_map_idx];
128 uint64_t start = new_map_info->start;
129 uint64_t end = new_map_info->end;
130 uint64_t flags = new_map_info->flags;
131 std::string* name = &new_map_info->name;
132 for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
133 MapInfo* info = maps_[old_map_idx];
134 if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
135 // No need to check
136 search_map_idx = old_map_idx + 1;
137 delete new_map_info;
138 maps_[new_map_idx] = nullptr;
139 total_entries--;
140 break;
141 } else if (info->start > start) {
142 // Stop, there isn't going to be a match.
143 search_map_idx = old_map_idx;
144 break;
145 }
146
147 // Never delete these maps, they may be in use. The assumption is
148 // that there will only every be a handfull of these so waiting
149 // to destroy them is not too expensive.
150 saved_maps_.push_back(info);
151 maps_[old_map_idx] = nullptr;
152 total_entries--;
153 }
154 if (search_map_idx >= last_map_idx) {
155 break;
156 }
157 }
158
159 // Now move out any of the maps that never were found.
160 for (size_t i = search_map_idx; i < last_map_idx; i++) {
161 saved_maps_.push_back(maps_[i]);
162 maps_[i] = nullptr;
163 total_entries--;
164 }
165
166 // Sort all of the values such that the nullptrs wind up at the end, then
167 // resize them away.
168 std::sort(maps_.begin(), maps_.end(), [](const auto* a, const auto* b) {
169 if (a == nullptr) {
170 return false;
171 } else if (b == nullptr) {
172 return true;
173 }
174 return a->start < b->start;
175 });
176 maps_.resize(total_entries);
177
178 return true;
179}
180
181LocalUpdatableMaps::~LocalUpdatableMaps() {
182 for (auto map_info : saved_maps_) {
183 delete map_info;
184 }
185}
186
108} // namespace unwindstack 187} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/LocalUnwinder.h b/libunwindstack/include/unwindstack/LocalUnwinder.h
new file mode 100644
index 000000000..80bb53ec1
--- /dev/null
+++ b/libunwindstack/include/unwindstack/LocalUnwinder.h
@@ -0,0 +1,86 @@
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#ifndef _LIBUNWINDSTACK_LOCAL_UNWINDER_H
18#define _LIBUNWINDSTACK_LOCAL_UNWINDER_H
19
20#include <pthread.h>
21#include <stdint.h>
22#include <sys/types.h>
23
24#include <memory>
25#include <string>
26#include <vector>
27
28#include <unwindstack/Error.h>
29#include <unwindstack/Maps.h>
30#include <unwindstack/Memory.h>
31
32namespace unwindstack {
33
34// Forward declarations.
35class Elf;
36struct MapInfo;
37
38struct LocalFrameData {
39 LocalFrameData(MapInfo* map_info, uint64_t pc, uint64_t rel_pc, const std::string& function_name,
40 uint64_t function_offset)
41 : map_info(map_info),
42 pc(pc),
43 rel_pc(rel_pc),
44 function_name(function_name),
45 function_offset(function_offset) {}
46
47 MapInfo* map_info;
48 uint64_t pc;
49 uint64_t rel_pc;
50 std::string function_name;
51 uint64_t function_offset;
52};
53
54// This is a specialized class that should only be used for doing local unwinds.
55// The Unwind call can be made as multiple times on the same object, and it can
56// be called by multiple threads at the same time.
57// It is designed to be used in debugging circumstances to get a stack trace
58// as fast as possible.
59class LocalUnwinder {
60 public:
61 LocalUnwinder() = default;
62 LocalUnwinder(const std::vector<std::string>& skip_libraries) : skip_libraries_(skip_libraries) {}
63 ~LocalUnwinder() = default;
64
65 bool Init();
66
67 bool Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames);
68
69 bool ShouldSkipLibrary(const std::string& map_name);
70
71 MapInfo* GetMapInfo(uint64_t pc);
72
73 ErrorCode LastErrorCode() { return last_error_.code; }
74 uint64_t LastErrorAddress() { return last_error_.address; }
75
76 private:
77 pthread_rwlock_t maps_rwlock_;
78 std::unique_ptr<LocalUpdatableMaps> maps_ = nullptr;
79 std::shared_ptr<Memory> process_memory_;
80 std::vector<std::string> skip_libraries_;
81 ErrorData last_error_;
82};
83
84} // namespace unwindstack
85
86#endif // _LIBUNWINDSTACK_LOCAL_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 74e5c4729..67fbed21a 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -87,6 +87,19 @@ class LocalMaps : public RemoteMaps {
87 virtual ~LocalMaps() = default; 87 virtual ~LocalMaps() = default;
88}; 88};
89 89
90class LocalUpdatableMaps : public Maps {
91 public:
92 LocalUpdatableMaps() : Maps() {}
93 virtual ~LocalUpdatableMaps();
94
95 bool Reparse();
96
97 const std::string GetMapsFile() const override;
98
99 private:
100 std::vector<MapInfo*> saved_maps_;
101};
102
90class BufferMaps : public Maps { 103class BufferMaps : public Maps {
91 public: 104 public:
92 BufferMaps(const char* buffer) : buffer_(buffer) {} 105 BufferMaps(const char* buffer) : buffer_(buffer) {}
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
new file mode 100644
index 000000000..56a18cde2
--- /dev/null
+++ b/libunwindstack/tests/LocalUnwinderTest.cpp
@@ -0,0 +1,207 @@
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
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
diff --git a/libunwindstack/tests/TestLocal.cpp b/libunwindstack/tests/TestLocal.cpp
new file mode 100644
index 000000000..fa0baff36
--- /dev/null
+++ b/libunwindstack/tests/TestLocal.cpp
@@ -0,0 +1,39 @@
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 <unwindstack/LocalUnwinder.h>
18
19#include <vector>
20
21extern "C" void TestlibLevel4(void* unwinder_data, void* frame_data) {
22 unwindstack::LocalUnwinder* unwinder =
23 reinterpret_cast<unwindstack::LocalUnwinder*>(unwinder_data);
24 std::vector<unwindstack::LocalFrameData>* frame_info =
25 reinterpret_cast<std::vector<unwindstack::LocalFrameData>*>(frame_data);
26 unwinder->Unwind(frame_info, 256);
27}
28
29extern "C" void TestlibLevel3(void* unwinder_data, void* frame_data) {
30 TestlibLevel4(unwinder_data, frame_data);
31}
32
33extern "C" void TestlibLevel2(void* unwinder_data, void* frame_data) {
34 TestlibLevel3(unwinder_data, frame_data);
35}
36
37extern "C" void TestlibLevel1(void* unwinder_data, void* frame_data) {
38 TestlibLevel2(unwinder_data, frame_data);
39}
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 242cc6a6f..83695bbcd 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -106,15 +106,12 @@ static void VerifyUnwind(pid_t pid, Maps* maps, Regs* regs,
106 Unwinder unwinder(512, maps, regs, process_memory); 106 Unwinder unwinder(512, maps, regs, process_memory);
107 unwinder.Unwind(); 107 unwinder.Unwind();
108 108
109 std::string expected_function = expected_function_names.back();
110 expected_function_names.pop_back();
111 for (auto& frame : unwinder.frames()) { 109 for (auto& frame : unwinder.frames()) {
112 if (frame.function_name == expected_function) { 110 if (frame.function_name == expected_function_names.back()) {
111 expected_function_names.pop_back();
113 if (expected_function_names.empty()) { 112 if (expected_function_names.empty()) {
114 break; 113 break;
115 } 114 }
116 expected_function = expected_function_names.back();
117 expected_function_names.pop_back();
118 } 115 }
119 } 116 }
120 117